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 2021/06/09 13:27:33 UTC
[sling-whiteboard] branch master updated: Use Folder and Document
POJOs (with lazy loading), simplify data fetchers, document the schema
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 d6b334a Use Folder and Document POJOs (with lazy loading), simplify data fetchers, document the schema
d6b334a is described below
commit d6b334aa9ac0c924d4cb321aafee2140bfa05873
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Wed Jun 9 15:27:16 2021 +0200
Use Folder and Document POJOs (with lazy loading), simplify data fetchers, document the schema
---
remote-content-api/sample-graphql-api/README.md | 90 ++++++++++---
.../remotecontent/contentmodel/Backstage.java | 42 ++++++
.../remotecontent/contentmodel/ContentItem.java | 43 +++++++
.../contentmodel/ContentItemHeader.java | 74 +++++++++++
.../sling/remotecontent/contentmodel/Document.java | 58 +++++++++
.../sling/remotecontent/contentmodel/Etc.java | 28 ++++
.../sling/remotecontent/contentmodel/Folder.java | 29 +++++
.../sling/remotecontent/contentmodel/Link.java | 39 ++++++
.../samples/graphql/DocumentDataFetcher.java | 34 +----
.../samples/graphql/DocumentsDataFetcher.java | 29 ++---
.../samples/graphql/FetcherContext.java | 51 ++++++++
.../samples/graphql/FolderDataFetcher.java | 37 +-----
.../samples/graphql/FoldersDataFetcher.java | 27 ++--
.../main/resources/schemas/default/N.GQLschema.jsp | 141 ++++++++++++++++++---
14 files changed, 587 insertions(+), 135 deletions(-)
diff --git a/remote-content-api/sample-graphql-api/README.md b/remote-content-api/sample-graphql-api/README.md
index acda76e..d6d5f25 100644
--- a/remote-content-api/sample-graphql-api/README.md
+++ b/remote-content-api/sample-graphql-api/README.md
@@ -32,32 +32,90 @@ we don't really care about the details of these node types besides their names.
## Example GraphQL queries
- {
- document(path:"/content/articles/music/eloy-hahn-on-the-system-of-1080p-et-corrupti-aka-xml", selectors: "not, used, sofar") {
- path
- selectors
- body
+ {
+ folders(limit: 55, after: "L2NvbnRlbnQvYXJ0aWNsZXMvbXVzaWM=") {
+ pageInfo {
+ endCursor
+ }
+ edges {
+ node {
+ path
+ header {
+ resourceType
+ title
+ }
+ }
+ }
}
}
-
+
{
- document(path:"/content/wknd/us/en/adventures/riverside-camping-australia", selectors: "not,used,yet") {
- path
- selectors
- body
+ folder {
+ path
+ header {
+ parent
+ resourceType
+ resourceSuperType
+ links {
+ rel
+ href
+ }
+ }
+ }
+ document {
+ path
+ header {
+ parent
+ }
}
}
{
- documents(query:"//content/*/*") {
+ folder(path: "/apps") {
path
- body
+ header {
+ parent
+ }
}
}
{
- documents(query:"//content//*[jcr:contains(.,'wknd')]") {
+ document {
path
+ header {
+ parent
+ resourceType
+ resourceSuperType
+ title
+ summary
+ description
+ links {
+ href
+ rel
+ }
+ etc
+ }
+ backstage {
+ authoring
+ publishing
+ etc
+ }
+ body
+ }
+ }
+
+ {
+ documents(query: "//content/*/*") {
+ edges {
+ node {
+ path
+ header {
+ parent
+ resourceType
+ resourceSuperType
+ }
+ }
+ }
}
}
@@ -80,12 +138,6 @@ we don't really care about the details of these node types besides their names.
}
{
- document(path:"/open-for-all") {
- body
- }
- }
-
- {
documents(
lang:"sql2020",
query:"""
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Backstage.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Backstage.java
new file mode 100644
index 0000000..84fccd7
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Backstage.java
@@ -0,0 +1,42 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+import java.util.HashMap;
+
+/** Backstage information is meant to provide hints and rules
+ * to authoring user interfaces and publishing services.
+ */
+public class Backstage extends HashMap<String, Object> {
+ private Etc etc;
+
+ public Backstage() {
+ put("authoring", "authoring/editor information can come here, free-form");
+ put("publishing", "publishing hints can come here, free-form");
+ }
+
+ public Object getEtc() {
+ if(etc == null) {
+ etc = new Etc();
+ etc.put("info", "...additional information for this Document");
+ }
+ return etc;
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/ContentItem.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/ContentItem.java
new file mode 100644
index 0000000..723cb85
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/ContentItem.java
@@ -0,0 +1,43 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+import org.apache.sling.api.resource.Resource;
+
+/** Base class for folders and documents */
+public class ContentItem {
+ protected final Resource resource;
+ protected ContentItemHeader header;
+
+ ContentItem(Resource r) {
+ resource = r;
+ }
+
+ public String getPath() {
+ return resource.getPath();
+ }
+
+ public ContentItemHeader getHeader() {
+ if(header == null) {
+ header = new ContentItemHeader(resource);
+ }
+ return header;
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/ContentItemHeader.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/ContentItemHeader.java
new file mode 100644
index 0000000..3436358
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/ContentItemHeader.java
@@ -0,0 +1,74 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sling.api.resource.Resource;
+
+/** Header for ContentItems */
+public class ContentItemHeader {
+ protected final Resource resource;
+ private List<Link> links;
+
+ ContentItemHeader(Resource r) {
+ resource = r;
+ }
+
+ private void setupLinksIfNeeded() {
+ if(links == null) {
+ links = new ArrayList<>();
+ links.add(new Link("self", resource.getPath()));
+ }
+ }
+
+ public Collection<Link> getLinks() {
+ setupLinksIfNeeded();
+ return Collections.unmodifiableList(links);
+ }
+
+ public String getResourceType() {
+ return resource.getResourceType();
+ }
+
+ public String getResourceSuperType() {
+ return resource.getResourceSuperType();
+ }
+
+ public String getParent() {
+ final Resource parent = resource.getParent();
+ return parent == null ? null : parent.getPath();
+ }
+
+ public String getTitle() {
+ return null;
+ }
+
+ public String getDescription() {
+ return null;
+ }
+
+ public String getSummary() {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Document.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Document.java
new file mode 100644
index 0000000..4e17508
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Document.java
@@ -0,0 +1,58 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+import org.apache.sling.api.resource.Resource;
+
+/** Base class for folders and documents */
+public class Document extends ContentItem {
+ private Backstage backstage;
+
+ public Document(Resource r) {
+ super(r);
+ }
+
+ public Object getBody() {
+ /*
+ @Reference(target="(" + DocumentTree.TARGET_TYPE + "=map)")
+ private DocumentTree mappingTarget;
+
+ @Reference
+ private DocumentAggregator documentAggregator;
+
+ // Use the aggregator to build the body
+ if(mappingTarget != null) {
+ final DocumentTree.DocumentNode body = mappingTarget.newDocumentNode();
+ final DocumentAggregator.Options opt = new DocumentAggregator.Options(false, new UrlBuilderStub());
+ documentAggregator.aggregate(r, body, opt);
+ body.close();
+ data.put("body", body.adaptTo(Map.class));
+ }
+ */
+ return "This will be the document body, created using the document aggregator or document-speficic services";
+ }
+
+ public Backstage getBackstage() {
+ if(backstage == null) {
+ backstage = new Backstage();
+ }
+ return backstage;
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Etc.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Etc.java
new file mode 100644
index 0000000..61b818b
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Etc.java
@@ -0,0 +1,28 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+import java.util.HashMap;
+
+/** Backstage information is meant to provide hints and rules
+ * to authoring user interfaces and publishing services.
+ */
+public class Etc extends HashMap<String, Object> {
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Folder.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Folder.java
new file mode 100644
index 0000000..c910f63
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Folder.java
@@ -0,0 +1,29 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+import org.apache.sling.api.resource.Resource;
+
+/** Base class for folders and documents */
+public class Folder extends ContentItem {
+ public Folder(Resource r) {
+ super(r);
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Link.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Link.java
new file mode 100644
index 0000000..2be52a3
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/contentmodel/Link.java
@@ -0,0 +1,39 @@
+/*
+ * 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.remotecontent.contentmodel;
+
+/** Link, with a relation and href */
+public class Link {
+ private final String relation;
+ private final String href;
+
+ public Link(String relation, String href) {
+ this.relation = relation;
+ this.href = href;
+ }
+
+ public String getRel() {
+ return relation;
+ }
+
+ public String getHref() {
+ return href;
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentDataFetcher.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentDataFetcher.java
index 8dc3332..0673793 100644
--- a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentDataFetcher.java
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentDataFetcher.java
@@ -19,44 +19,18 @@
package org.apache.sling.remotecontent.samples.graphql;
-import java.util.Map;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.documentaggregator.api.DocumentAggregator;
-import org.apache.sling.documentaggregator.api.DocumentTree;
import org.apache.sling.graphql.api.SlingDataFetcher;
import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
+import org.apache.sling.remotecontent.contentmodel.Document;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
@Component(service = SlingDataFetcher.class, property = {"name=samples/document"})
-public class DocumentDataFetcher implements SlingDataFetcher<Object> {
-
- @Reference(target="(" + DocumentTree.TARGET_TYPE + "=map)")
- private DocumentTree mappingTarget;
-
- @Reference
- private DocumentAggregator documentAggregator;
-
- protected Map<String, Object> toDocument(Resource r) {
- final Map<String, Object> data = FolderDataFetcher.toDocument(r);
- //final DocumentAggregator.Options opt = new DocumentAggregator.Options(e.getArgument("debug", false), new UrlBuilderStub());
-
- // Use the aggregator to build the body
- if(mappingTarget != null) {
- final DocumentTree.DocumentNode body = mappingTarget.newDocumentNode();
- final DocumentAggregator.Options opt = new DocumentAggregator.Options(false, new UrlBuilderStub());
- documentAggregator.aggregate(r, body, opt);
- body.close();
- data.put("body", body.adaptTo(Map.class));
- }
- return data;
- }
+public class DocumentDataFetcher implements SlingDataFetcher<Document> {
@Override
- public @Nullable Object get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
- return toDocument(FolderDataFetcher.getTargetResource(e));
+ public @Nullable Document get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
+ return new Document(new FetcherContext(e, false).currentResource);
}
}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentsDataFetcher.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentsDataFetcher.java
index 4bfb12a..bad235c 100644
--- a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentsDataFetcher.java
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/DocumentsDataFetcher.java
@@ -20,25 +20,23 @@
package org.apache.sling.remotecontent.samples.graphql;
import java.util.Iterator;
-import java.util.Map;
-import java.util.function.Function;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.graphql.api.SlingDataFetcher;
import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
-import org.apache.sling.graphql.api.pagination.Cursor;
+import org.apache.sling.graphql.api.pagination.Connection;
import org.apache.sling.graphql.helpers.GenericConnection;
+import org.apache.sling.remotecontent.contentmodel.Document;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.service.component.annotations.Component;
@Component(service = SlingDataFetcher.class, property = {"name=samples/documents"})
-public class DocumentsDataFetcher extends DocumentDataFetcher {
+public class DocumentsDataFetcher implements SlingDataFetcher<Connection<Document>> {
@Override
- public @Nullable Object get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
- final int limit = e.getArgument("limit", 5);
- final String after = e.getArgument("after", null);
+ public @Nullable Connection<Document> get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
+ final FetcherContext ctx = new FetcherContext(e, false);
// Use a suffix as we might not keep these built-in language in the long term
final String langSuffix = "2020";
@@ -50,14 +48,11 @@ public class DocumentsDataFetcher extends DocumentDataFetcher {
lang = lang.replaceAll(langSuffix + "$", "");
final String query = e.getArgument("query");
- final Iterator<Resource> resultIterator = e.getCurrentResource().getResourceResolver().findResources(query, lang);
- final Function<Map<String, Object>, String> cursorStringProvider = data -> (String)data.get("path");
- final Function<Resource, Map<String, Object>> converter = this::toDocument;
- final Iterator<Map<String, Object>> it = new ConvertingIterator<>(resultIterator, converter);
- return new GenericConnection.Builder<>(it, cursorStringProvider)
- .withStartAfter(Cursor.fromEncodedString(after))
- .withLimit(limit)
+ final Iterator<Resource> resultIterator = ctx.currentResource.getResourceResolver().findResources(query, lang);
+ final Iterator<Document> it = new ConvertingIterator<>(resultIterator, Document::new);
+ return new GenericConnection.Builder<>(it, Document::getPath)
+ .withStartAfter(ctx.afterCursor)
+ .withLimit(ctx.limit)
.build();
- }
-
-}
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FetcherContext.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FetcherContext.java
new file mode 100644
index 0000000..63dbbe5
--- /dev/null
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FetcherContext.java
@@ -0,0 +1,51 @@
+/*
+ * 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.remotecontent.samples.graphql;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
+import org.apache.sling.graphql.api.pagination.Cursor;
+
+class FetcherContext {
+ public static final String LIMIT_ARG = "limit";
+ public static final String AFTER_ARG = "after";
+ public static final String PATH_ARG = "path";
+ public static final int DEFAULT_LIMIT = 50;
+
+ public final Resource currentResource;
+ public final Cursor afterCursor;
+ public final int limit;
+
+ FetcherContext(SlingDataFetcherEnvironment e, boolean includePagination) {
+ Resource r = e.getCurrentResource();
+ final String path = e.getArgument(PATH_ARG);
+ if(path != null && !path.isEmpty()) {
+ r = r.getResourceResolver().getResource(path);
+ }
+ currentResource = r;
+ if(includePagination) {
+ afterCursor = Cursor.fromEncodedString(e.getArgument(AFTER_ARG));
+ limit = e.getArgument(LIMIT_ARG, DEFAULT_LIMIT);
+ } else {
+ afterCursor = null;
+ limit = -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FolderDataFetcher.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FolderDataFetcher.java
index 494953d..23b0bf5 100644
--- a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FolderDataFetcher.java
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FolderDataFetcher.java
@@ -19,13 +19,9 @@
package org.apache.sling.remotecontent.samples.graphql;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.graphql.api.SlingDataFetcher;
import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
+import org.apache.sling.remotecontent.contentmodel.Folder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.service.component.annotations.Component;
@@ -33,37 +29,8 @@ import org.osgi.service.component.annotations.Component;
@Component(service = SlingDataFetcher.class, property = {"name=samples/folder"})
public class FolderDataFetcher implements SlingDataFetcher<Object> {
- // TODO move to document converter + make body optional
- static Map<String, Object> toDocument(Resource r) {
- final Map<String, Object> data = new HashMap<>();
- data.put("path", r.getPath());
- data.put("header", toDocumentHeader(r));
- data.put("properties", r.adaptTo(ValueMap.class));
- return data;
- }
-
- // TODO move to document converter + make body optional
- static Map<String, Object> toDocumentHeader(Resource r) {
- final Map<String, Object> header = new HashMap<>();
- if(r.getParent() != null) {
- header.put("parent", r.getParent().getPath());
- }
- header.put("resourceType", r.getResourceType());
- header.put("resourceSuperType", r.getResourceSuperType());
- return header;
- }
-
- static Resource getTargetResource(SlingDataFetcherEnvironment e) {
- Resource result = e.getCurrentResource();
- String path = e.getArgument("path");
- if(path != null && !path.isEmpty()) {
- result = result.getResourceResolver().getResource(path);
- }
- return result;
- }
-
@Override
public @Nullable Object get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
- return toDocument(getTargetResource(e));
+ return new Folder(new FetcherContext(e, false).currentResource);
}
}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FoldersDataFetcher.java b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FoldersDataFetcher.java
index 25cd7fb..194b7af 100644
--- a/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FoldersDataFetcher.java
+++ b/remote-content-api/sample-graphql-api/src/main/java/org/apache/sling/remotecontent/samples/graphql/FoldersDataFetcher.java
@@ -20,38 +20,33 @@
package org.apache.sling.remotecontent.samples.graphql;
import java.util.Iterator;
-import java.util.Map;
-import java.util.function.Function;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.graphql.api.SlingDataFetcher;
import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
-import org.apache.sling.graphql.api.pagination.Cursor;
+import org.apache.sling.graphql.api.pagination.Connection;
import org.apache.sling.graphql.helpers.GenericConnection;
+import org.apache.sling.remotecontent.contentmodel.Folder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.service.component.annotations.Component;
@Component(service = SlingDataFetcher.class, property = {"name=samples/folders"})
-public class FoldersDataFetcher implements SlingDataFetcher<Object> {
+public class FoldersDataFetcher implements SlingDataFetcher<Connection<Folder>> {
@Override
- public @Nullable Object get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
- final int limit = e.getArgument("limit", 5);
- final String after = e.getArgument("after", null);
+ public @Nullable Connection<Folder> get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
+ final FetcherContext ctx = new FetcherContext(e, false);
- final Resource context = FolderDataFetcher.getTargetResource(e);
final String xpathQuery = String.format(
"/jcr:root%s//element(*, nt:folder) order by jcr:path ascending option(traversal fail)",
- context.getPath());
+ ctx.currentResource.getPath());
- final Iterator<Resource> resultIterator = context.getResourceResolver().findResources(xpathQuery, "xpath");
- final Function<Map<String, Object>, String> cursorStringProvider = data -> (String)data.get("path");
- final Function<Resource, Map<String, Object>> converter = FolderDataFetcher::toDocument;
- final Iterator<Map<String, Object>> it = new ConvertingIterator<>(resultIterator, converter);
- return new GenericConnection.Builder<>(it, cursorStringProvider)
- .withStartAfter(Cursor.fromEncodedString(after))
- .withLimit(limit)
+ final Iterator<Resource> resultIterator = ctx.currentResource.getResourceResolver().findResources(xpathQuery, "xpath");
+ final Iterator<Folder> it = new ConvertingIterator<>(resultIterator, Folder::new);
+ return new GenericConnection.Builder<>(it, Folder::getPath)
+ .withStartAfter(ctx.afterCursor)
+ .withLimit(ctx.limit)
.build();
}
}
\ No newline at end of file
diff --git a/remote-content-api/sample-graphql-api/src/main/resources/schemas/default/N.GQLschema.jsp b/remote-content-api/sample-graphql-api/src/main/resources/schemas/default/N.GQLschema.jsp
index 06f5d53..db56f61 100644
--- a/remote-content-api/sample-graphql-api/src/main/resources/schemas/default/N.GQLschema.jsp
+++ b/remote-content-api/sample-graphql-api/src/main/resources/schemas/default/N.GQLschema.jsp
@@ -17,46 +17,151 @@
* under the License.
--%>
-<%-- N plane schema: Navigation --%>
+<%--
+N plane schema: Navigation
+There's no real need for this to be a JSP script, we might want to
+create a "text passthrough" script engine for such things.
+--%>
+
+"""
+Some fields use this Scalar to provide unstructured or semi-structured data
+"""
scalar Object
type Query {
+ """
+ Query a single Folder.
+ If not specified, the path defaults to the Resource which receives the query request.
+ """
folder(path: String) : Folder @fetcher(name:"samples/folder")
+
+ """
+ Paginated query for multiple Folders.
+ """
folders(path: String, limit: Int, after: String) : FolderConnection @connection(for: "Folder") @fetcher(name:"samples/folders")
- document(path : String, selectors : [String], debug : Boolean) : Document @fetcher(name:"samples/document")
- documents(lang: String, query : String, debug: Boolean, limit: Int, after: String) : DocumentConnection @connection(for: "Document") @fetcher(name:"samples/documents")
-}
-type Mutation {
- command(lang: String, script: String) : CommandResult @fetcher(name:"samples/command")
+ """
+ Query a single Document. If not specified, the path defaults to the Resource which receives the query request
+ """
+ document(path : String) : Document @fetcher(name:"samples/document")
+
+ """
+ Paginated query for multiple documents.
+ 'lang' indicates the query language - TODO provide a query to list those languages and their documentation
+ """
+ documents(lang: String, query : String, limit: Int, after: String) : DocumentConnection @connection(for: "Document") @fetcher(name:"samples/documents")
}
+"""
+A Folder can contain other Folders or Documents
+"""
type Folder {
- path : String
- header : DocumentHeader
+ path : ID!
+ header : ContentItemHeader!
}
-type DocumentHeader {
+"""
+Common header for Folders and Documents
+"""
+type ContentItemHeader {
+ """ path of the parent Folder or Document """
parent : String
+
+ """ The resource type of this document, can be used for example to select publishing templates """
resourceType : String
+
+ """ The resource supertype provides a simple form of inheritance, for templating fallbacks for example """
resourceSuperType : String
+
+ """ The title of this document, if supplied """
+ title : String
+
+ """ The description of this document, if supplied """
+ description : String
+
+ """ The summary of this document, if supplied """
+ summary : String
+ links : [Link]
+
+ """
+ etc can contain any additional information, as an unstructured Object scalar
+ """
+ etc : Object
}
+"""
+A Document represents content that's usually meant for authoring and publishing
+"""
type Document {
- path : String
- header : DocumentHeader
- properties : Object
+ path : ID!
+ header : ContentItemHeader!
+ backstage : Backstage
+
+ """
+ The document's body is unstructured, usually generated by a document aggregator service.
+ Fields like 'resourceType' in that content can help rendering or applying UI logic to it.
+ """
body : Object
}
-type CommandResult {
- success: Boolean
- output: String
- help: String
- links: [Link]
+"""
+Backstage is for data related to authoring or publishing content:
+authoring rules, publishing hints etc.
+"""
+
+type Backstage {
+ authoring : Object
+ publishing : Object
+
+ """
+ etc can contain any additional information, as an unstructured Object scalar
+ """
+ etc : Object
}
+"""
+A link with its relationship and href
+"""
type Link {
+ """
+ The link relationship
+ TODO defined standard values for this
+ """
rel: String
- url: String
+
+ """
+ The link's href, as would be used in HTML
+ """
+ href: String!
+}
+
+"""
+Commands can be sent using this Mutation
+"""
+type Mutation {
+ """
+ 'lang' is the command language - TODO provide a query that lists languages with their help text
+ 'script' is the script to execute, in the language indicated by 'lang'
+ """
+ command(lang: String, script: String) : CommandResult @fetcher(name:"samples/command")
+}
+
+"""
+The result of executing a command
+"""
+type CommandResult {
+ """ true if the command was successful (TODO use status/error codes?) """
+ success: Boolean!
+
+ """ The command output, as text """
+ output: String
+
+ """ Optional help text for this command """
+ help: String
+
+ """
+ Links can point to resources that the command created, for example,
+ or to the documentation of the command itself, or its language.
+ """
+ links: [Link]
}
\ No newline at end of file