Add a random JSON structure in query results, to simulate unstructured content
diff --git a/org.apache.sling.graphql.samples.website/README.md b/org.apache.sling.graphql.samples.website/README.md
index 3bef8c0..b1ddef4 100644
--- a/org.apache.sling.graphql.samples.website/README.md
+++ b/org.apache.sling.graphql.samples.website/README.md
@@ -88,6 +88,50 @@
<%@include file="/apps/samples/common/GQLschema.jsp" %>
+
+## Unstructured content
+
+Sling applications often deal with unstructred or semi-structured content which
+doesn't have a strict schema.
+
+To demonstrate how this works with GraphQL queries, in this sample you can use a
+query such as
+
+ {
+ navigation {
+ root
+ }
+ random
+ }
+
+Which includes a randomly generated hierarchical structure, to test how GraphQL
+clients cope (they should - it's part of the standard) with results such as
+
+ {
+ "data": {
+ "navigation": {
+ "root": "/content/articles"
+ },
+ "random": {
+ "key1": 112,
+ "sub2": {
+ "key1": false,
+ "sub2": {
+ "key1": true
+ },
+ "key5": [
+ true,
+ true
+ ],
+ "key3": true
+ }
+ }
+ }
+ }
+
+where the "shape" and content of the `random` element can vary widely, simulating
+varying content structures.
+
## How to run this
Build and run with
diff --git a/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/RandomDataFetcher.java b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/RandomDataFetcher.java
new file mode 100644
index 0000000..c0f3cc4
--- /dev/null
+++ b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/datafetchers/RandomDataFetcher.java
@@ -0,0 +1,78 @@
+/*
+ * 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.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+import org.apache.sling.graphql.api.SlingDataFetcher;
+import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
+import org.osgi.service.component.annotations.Component;
+
+/** Return a nested Map of random data, for testing unpredictable structures */
+@Component(service = SlingDataFetcher.class, property = {"name=website/random"})
+public class RandomDataFetcher implements SlingDataFetcher<Object> {
+
+ private final Random random = new Random(42);
+
+ private Object[] randomArray() {
+ final Object[] result = new Object[random.nextInt(3) + 1];
+ for(int i=0; i < result.length; i++) {
+ result[i] = randomValue();
+ }
+ return result;
+ }
+
+ private Object randomValue() {
+ switch(random.nextInt(4)) {
+ case 0: return "It is now " + new Date();
+ case 1: return random.nextInt(2) > 0;
+ case 2: return randomArray();
+ default: return random.nextInt(451);
+ }
+ }
+
+ private Map<String, Object> randomMap(int maxEntries) {
+ int counter=1;
+ final Map<String, Object> result = new HashMap<>();
+ while(maxEntries > 0) {
+ result.put("key" + counter++, randomValue());
+ if(random.nextInt(2) == 1) {
+ final int maxSub = random.nextInt(maxEntries) / (random.nextInt(2) + 1);
+ result.put("sub" + counter++, randomMap(maxSub));
+ maxEntries -= maxSub;
+ }
+ maxEntries--;
+ }
+ return result;
+ }
+
+ @Override
+ public Object get(SlingDataFetcherEnvironment env) throws Exception {
+ Map<String, Object> result = randomMap(random.nextInt(24));
+ result.put(
+ "info",
+ "The contents of this map are random, to demonstrate unpredictable content structures"
+ );
+ 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/scalars/ObjectScalar.java b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/scalars/ObjectScalar.java
new file mode 100644
index 0000000..79271c8
--- /dev/null
+++ b/org.apache.sling.graphql.samples.website/src/main/java/org/apache/sling/graphql/samples/website/scalars/ObjectScalar.java
@@ -0,0 +1,41 @@
+/*
+ * 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.scalars;
+
+import org.apache.sling.graphql.api.ScalarConversionException;
+import org.apache.sling.graphql.api.SlingScalarConverter;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.service.component.annotations.Component;
+
+/** Passthrough Scalar, used to output unpredictable JSON structures */
+@Component(service = SlingScalarConverter.class, property = { "name=Object" })
+public class ObjectScalar implements SlingScalarConverter<Object, Object> {
+
+ @Override
+ public @Nullable Object parseValue(@Nullable Object input) throws ScalarConversionException {
+ return input;
+ }
+
+ @Override
+ public @Nullable Object serialize(@Nullable Object value) throws ScalarConversionException {
+ return value;
+ }
+
+}
\ No newline at end of file
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
index a085489..d2313a3 100644
--- 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
@@ -19,11 +19,16 @@
<%@include file="../common/directives.jsp" %>
+scalar Object
+
type Query {
<%@include file="../common/common-query-parts.jsp" %>
# List of Articles which contain the supplied text
article (withText : String) : [Article] @fetcher(name:"website/articlesWithText")
+
+ # Testing a Scalar that returns an unpredictable JSON structure
+ random : Object @fetcher(name:"website/random")
}
<%@include file="/apps/samples/common/GQLschema.jsp" %>
\ No newline at end of file