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 10c48a9 SLING-10551 - partials wiring and selection works, no schema
'parser' yet
10c48a9 is described below
commit 10c48a949b416a4e2d66b3db9edeeaed9a03103b
Author: Bertrand Delacretaz <[email protected]>
AuthorDate: Wed Jul 7 16:30:29 2021 +0200
SLING-10551 - partials wiring and selection works, no schema 'parser' yet
---
sling-org-apache-sling-graphql-schema/pom.xml | 2 +-
...ovider.java => BundleEntryPartialProvider.java} | 29 ++++----
.../aggregator/impl/DefaultSchemaAggregator.java | 77 ++++++++++++++++++----
.../{api => impl}/PartialSchemaProvider.java | 4 +-
.../aggregator/impl/ProviderBundleTracker.java | 21 ++++--
.../servlet/SchemaAggregatorServlet.java | 49 +++++++++++++-
.../graphql/schema/aggregator/LogCapture.java | 70 ++++++++++++++++++++
.../graphql/schema/aggregator/{impl => }/U.java | 47 +++++++++++--
.../impl/BundleEntryPartialProviderTest.java | 34 ++++++++++
.../impl/DefaultSchemaAggregatorTest.java | 21 ++++--
.../aggregator/impl/ProviderBundleTrackerTest.java | 26 ++++++--
.../impl/SchemaAggregatorServletTest.java | 58 ++++++++++++++++
.../aggregator/it/SchemaAggregatorServletIT.java | 19 ++++--
.../test/resources/several-providers-output.txt | 35 ++++++++++
.../src/test/resources/two-providers-output.txt | 27 --------
15 files changed, 437 insertions(+), 82 deletions(-)
diff --git a/sling-org-apache-sling-graphql-schema/pom.xml
b/sling-org-apache-sling-graphql-schema/pom.xml
index 7329b3b..3785c6d 100644
--- a/sling-org-apache-sling-graphql-schema/pom.xml
+++ b/sling-org-apache-sling-graphql-schema/pom.xml
@@ -96,7 +96,7 @@
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes>
- <exclude>src/test/resources/two-providers-output.txt</exclude>
+ <exclude>src/test/resources/several-providers-output.txt</exclude>
</excludes>
</configuration>
</plugin>
diff --git
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntrySchemaProvider.java
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialProvider.java
similarity index 73%
rename from
sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntrySchemaProvider.java
rename to
sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialProvider.java
index 15388ed..027d9dc 100644
---
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntrySchemaProvider.java
+++
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialProvider.java
@@ -23,7 +23,6 @@ import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
-import org.apache.sling.graphql.schema.aggregator.api.PartialSchemaProvider;
import org.jetbrains.annotations.NotNull;
import org.osgi.framework.Bundle;
import org.slf4j.Logger;
@@ -32,33 +31,41 @@ import org.slf4j.LoggerFactory;
/** {@PartialSchemaProvider} build out of a Bundle entry, which must be a valid
* partial schema file.
*/
-class BundleEntrySchemaProvider implements PartialSchemaProvider {
- private static final Logger log =
LoggerFactory.getLogger(BundleEntrySchemaProvider.class.getName());
- private final URL url;
+class BundleEntryPartialProvider implements PartialSchemaProvider {
+ private static final Logger log =
LoggerFactory.getLogger(BundleEntryPartialProvider.class.getName());
private final String key;
private final long bundleId;
+ private final String name;
- private BundleEntrySchemaProvider(Bundle b, URL bundleEntry) {
- this.url = bundleEntry;
+ private BundleEntryPartialProvider(Bundle b, URL bundleEntry) {
this.bundleId = b.getBundleId();
this.key = String.format("%s(%d):%s", b.getSymbolicName(),
b.getBundleId(), bundleEntry.toString());
+ this.name = getPartialName(bundleEntry);
}
- static BundleEntrySchemaProvider forBundle(Bundle b, String entryPath) {
+ /** The partial's name is whatever follows the last slash, excluding the
file extension */
+ static String getPartialName(URL url) {
+ final String [] parts = url.toString().split("/");
+ String result = parts[parts.length - 1];
+ final int lastDot = result.lastIndexOf(".");
+ return lastDot > 0 ? result.substring(0, lastDot) : result;
+ }
+
+ static BundleEntryPartialProvider forBundle(Bundle b, String entryPath) {
final URL entry = b.getEntry(entryPath);
if(entry == null) {
log.info("Entry {} not found for bundle {}", entryPath,
b.getSymbolicName());
return null;
} else {
// TODO validate entry?
- return new BundleEntrySchemaProvider(b, entry);
+ return new BundleEntryPartialProvider(b, entry);
}
}
@Override
public boolean equals(Object other) {
- if(other instanceof BundleEntrySchemaProvider) {
- return ((BundleEntrySchemaProvider)other).key.equals(key);
+ if(other instanceof BundleEntryPartialProvider) {
+ return ((BundleEntryPartialProvider)other).key.equals(key);
}
return false;
}
@@ -73,7 +80,7 @@ class BundleEntrySchemaProvider implements
PartialSchemaProvider {
}
public String getName() {
- return url.toString();
+ return name;
}
public long getBundleId() {
diff --git
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregator.java
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregator.java
index 1b63c1a..5c9a0de 100644
---
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregator.java
+++
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregator.java
@@ -24,24 +24,45 @@ import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.Arrays;
+import java.util.HashSet;
import org.apache.commons.io.IOUtils;
-import org.apache.sling.graphql.schema.aggregator.api.PartialSchemaProvider;
import org.apache.sling.graphql.schema.aggregator.api.SchemaAggregator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.util.tracker.BundleTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Component(service = SchemaAggregator.class)
public class DefaultSchemaAggregator implements SchemaAggregator {
+ private static final Logger log =
LoggerFactory.getLogger(DefaultSchemaAggregator.class.getName());
private ProviderBundleTracker tracker;
+ private ServiceRegistration<?> trackerRegistration;
@Activate
public void activate(BundleContext ctx) {
tracker = new ProviderBundleTracker(ctx);
+ trackerRegistration = ctx.registerService(BundleTracker.class,
tracker, null);
+ tracker.open();
}
- private void copySection(List<PartialSchemaProvider> selected, String
sectionName, Writer target) throws IOException {
+ @Deactivate
+ public void deactivate(BundleContext ctx) {
+ if(trackerRegistration != null) {
+ tracker.close();
+ trackerRegistration.unregister();
+ trackerRegistration = null;
+ }
+ }
+
+ private void copySection(Set<PartialSchemaProvider> selected, String
sectionName, Writer target) throws IOException {
target.write(String.format("%n%s {%n", sectionName));
for(PartialSchemaProvider p : selected) {
writeSourceInfo(target, p);
@@ -55,24 +76,27 @@ public class DefaultSchemaAggregator implements
SchemaAggregator {
target.write(String.format("%n# %s.source=%s%n",
getClass().getSimpleName(), psp.getName()));
}
- public void aggregate(Writer target, String ...providerNames) throws
IOException {
+ /** Aggregate the schemas supplied by providers which match the exact
names or patterns supplied
+ * @param target where to write the output
+ * @param providerNamesOrRegexp a value that starts and ends with a slash
is used a a regular
+ * expression to match provider names (after removing the starting
and ending slash), other
+ * values are used as exact provider names.
+ * @throws IOException if an exact provider name is not found
+ */
+ public void aggregate(Writer target, String ...providerNamesOrRegexp)
throws IOException {
final String info = String.format("Schema aggregated by %s%n",
getClass().getSimpleName());
target.write(String.format("# %s", info));
// build list of selected providers
final Map<String, PartialSchemaProvider> providers =
tracker.getSchemaProviders();
- final List<PartialSchemaProvider> selected = new ArrayList<>();
- final List<String> missing = new ArrayList<>();
- for(String provider : providerNames) {
- final PartialSchemaProvider psp = providers.get(provider);
- if(psp == null) {
- missing.add(provider);
- continue;
- }
- selected.add(psp);
+ if(log.isDebugEnabled()) {
+ log.debug("Aggregating schemas, request={}, providers={}",
Arrays.asList(providerNamesOrRegexp), providers.keySet());
}
+ final Set<String> missing = new HashSet<>();
+ final Set<PartialSchemaProvider> selected = selectProviders(providers,
missing, providerNamesOrRegexp);
if(!missing.isEmpty()) {
+ log.debug("Requested providers {} not found in {}", missing,
providers.keySet());
throw new IOException(String.format("Missing providers: %s",
missing));
}
@@ -85,4 +109,33 @@ public class DefaultSchemaAggregator implements
SchemaAggregator {
}
target.write(String.format("%n# End of %s", info));
}
+
+ static Set<PartialSchemaProvider> selectProviders(Map<String,
PartialSchemaProvider> providers, Set<String> missing, String ...
providerNamesOrRegexp) {
+ final Set<PartialSchemaProvider> result= new HashSet<>();
+ for(String str : providerNamesOrRegexp) {
+ final Pattern p = toRegexp(str);
+ if(p != null) {
+ log.debug("Selecting providers matching {}", p);
+ providers.entrySet().stream()
+ .filter(e -> p.matcher(e.getKey()).matches())
+ .forEach(e -> result.add(e.getValue()));
+ } else {
+ log.debug("Selecting provider with key={}", str);
+ final PartialSchemaProvider psp = providers.get(str);
+ if(psp == null) {
+ missing.add(str);
+ continue;
+ }
+ result.add(psp);
+ }
+ }
+ return result;
+ }
+
+ static Pattern toRegexp(String input) {
+ if(input.startsWith("/") && input.endsWith("/")) {
+ return Pattern.compile(input.substring(1, input.length() - 1));
+ }
+ return null;
+ }
}
diff --git
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/api/PartialSchemaProvider.java
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialSchemaProvider.java
similarity index 93%
rename from
sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/api/PartialSchemaProvider.java
rename to
sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialSchemaProvider.java
index 95cc55b..c25fd12 100644
---
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/api/PartialSchemaProvider.java
+++
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialSchemaProvider.java
@@ -17,7 +17,7 @@
~ under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.graphql.schema.aggregator.api;
+package org.apache.sling.graphql.schema.aggregator.impl;
import java.io.Reader;
@@ -27,7 +27,7 @@ import org.osgi.annotation.versioning.ProviderType;
/** A provider of partial OSGI schemas */
@ProviderType
public interface PartialSchemaProvider {
- /** A unique name for this provider */
+ /** A unique name for this partial */
@NotNull String getName();
/** Return a Reader that provides the contents of the specific schema
section, like "query" or "mutation" */
diff --git
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTracker.java
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTracker.java
index 5349ce7..2abc914 100644
---
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTracker.java
+++
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTracker.java
@@ -24,7 +24,6 @@ import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.sling.graphql.schema.aggregator.api.PartialSchemaProvider;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
@@ -40,7 +39,7 @@ public class ProviderBundleTracker extends
BundleTracker<Object> {
public static final String SCHEMA_PATH_HEADER = "Sling-GraphQL-Schema";
private final Logger log = LoggerFactory.getLogger(getClass().getName());
- private final Map<String, BundleEntrySchemaProvider> schemaProviders;
+ private final Map<String, BundleEntryPartialProvider> schemaProviders;
public ProviderBundleTracker(BundleContext context) {
super(context, Bundle.ACTIVE, null);
@@ -53,20 +52,28 @@ public class ProviderBundleTracker extends
BundleTracker<Object> {
if(providersPath == null) {
log.debug("Bundle {} has no {} header, ignored",
bundle.getSymbolicName(), SCHEMA_PATH_HEADER);
} else {
+ // For now we only support file entries which are directly under
providersPath
final Enumeration<String> paths =
bundle.getEntryPaths(providersPath);
if(paths != null) {
while(paths.hasMoreElements()) {
- final BundleEntrySchemaProvider a =
BundleEntrySchemaProvider.forBundle(bundle, paths.nextElement());
- if(a != null) {
- log.info("Registering {}", a);
- schemaProviders.put(a.getName(), a);
- }
+
addIfNotPresent(BundleEntryPartialProvider.forBundle(bundle,
paths.nextElement()));
}
}
}
return super.addingBundle(bundle, event);
}
+ private void addIfNotPresent(BundleEntryPartialProvider a) {
+ if(a != null) {
+ if(schemaProviders.containsKey(a.getName())) {
+ log.warn("Partial provider with name {} already present, new
one will be ignored", a.getName());
+ } else {
+ log.info("Registering {}", a);
+ schemaProviders.put(a.getName(), a);
+ }
+ }
+ }
+
@Override
public void removedBundle(Bundle bundle, BundleEvent event, Object object)
{
final long id = bundle.getBundleId();
diff --git
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/servlet/SchemaAggregatorServlet.java
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/servlet/SchemaAggregatorServlet.java
index a031b69..a0e7d1c 100644
---
a/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/servlet/SchemaAggregatorServlet.java
+++
b/sling-org-apache-sling-graphql-schema/src/main/java/org/apache/sling/graphql/schema/aggregator/servlet/SchemaAggregatorServlet.java
@@ -20,6 +20,9 @@
package org.apache.sling.graphql.schema.aggregator.servlet;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
@@ -28,12 +31,16 @@ import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.graphql.schema.aggregator.api.SchemaAggregator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Component(
service = Servlet.class,
@@ -48,6 +55,8 @@ import
org.osgi.service.metatype.annotations.ObjectClassDefinition;
public class SchemaAggregatorServlet extends SlingSafeMethodsServlet {
+ private final Logger log = LoggerFactory.getLogger(getClass().getName());
+
@ObjectClassDefinition(
name = "Apache Sling GraphQL Schema Aggregator Servlet",
description = "Servlet that aggregates GraphQL schemas")
@@ -71,11 +80,39 @@ public class SchemaAggregatorServlet extends
SlingSafeMethodsServlet {
name = "Extensions",
description="Standard Sling servlet property")
String[] sling_servlet_extensions() default "GQLschema";
+
+ @AttributeDefinition(
+ name = "Selectors to partials mapping",
+ description="Each entry is in the format S:P1,P2,... where S is a
selector and P* the names of the corresponding schema partials")
+ String[] selectors_to_partials_mapping() default {};
+
}
@Reference
private transient SchemaAggregator aggregator;
+ private Map<String, String[]> selectorsToPartialNames = new HashMap<>();
+
+ @Activate
+ public void activate(BundleContext ctx, Config cfg) {
+ for(String str : cfg.selectors_to_partials_mapping()) {
+ final String [] parts = str.split("[:,]");
+ if(parts.length < 2) {
+ log.warn("Invalid selectors_to_partials_mapping configuration
string [{}]", str);
+ continue;
+ }
+ final String selector = parts[0].trim();
+ final String [] names = new String[parts.length - 1];
+ for(int i=1; i < parts.length; i++) {
+ names[i-1] = parts[i].trim();
+ }
+ if(log.isInfoEnabled()) {
+ log.info("Registering selector mapping: {} -> {}", selector,
Arrays.asList(names));
+ }
+ selectorsToPartialNames.put(selector, names);
+ }
+ }
+
@Override
public void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response) throws IOException {
final String [] selectors =
request.getRequestPathInfo().getSelectors();
@@ -86,6 +123,16 @@ public class SchemaAggregatorServlet extends
SlingSafeMethodsServlet {
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
- aggregator.aggregate(response.getWriter(), selectors);
+
+ final String key = selectors[0];
+ final String[] partialNames = selectorsToPartialNames.get(key);
+ if(partialNames == null) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "No partial
names defined for selector " + key);
+ return;
+ }
+ if(log.isDebugEnabled()) {
+ log.debug("Selector {} maps to partial names {}", key,
Arrays.asList(partialNames));
+ }
+ aggregator.aggregate(response.getWriter(), partialNames);
}
}
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/LogCapture.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/LogCapture.java
new file mode 100644
index 0000000..312e8a9
--- /dev/null
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/LogCapture.java
@@ -0,0 +1,70 @@
+/*
+ * 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.schema.aggregator;
+
+import static org.junit.Assert.fail;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+
+/** Capture logs for testing */
+public class LogCapture extends ListAppender<ILoggingEvent> implements
Closeable {
+ private final boolean verboseFailure;
+
+ /** Setup the capture and start it */
+ public LogCapture(String loggerName, boolean verboseFailure) {
+ this.verboseFailure = verboseFailure;
+ Logger logger = (Logger) LoggerFactory.getLogger(loggerName);
+ logger.setLevel(Level.ALL);
+ setContext((LoggerContext) LoggerFactory.getILoggerFactory());
+ logger.addAppender(this);
+ start();
+ }
+
+ public boolean anyMatch(Predicate<ILoggingEvent> p) {
+ return this.list.stream().anyMatch(p);
+ }
+
+ public void assertContains(Level atLevel, String ... substrings) {
+ Stream.of(substrings).forEach(substring -> {
+ if(!anyMatch(event -> event.getLevel() == atLevel &&
event.getFormattedMessage().contains(substring))) {
+ if(verboseFailure) {
+ fail(String.format("No log message contains [%s] in
log\n%s", substring, this.list.toString()));
+ } else {
+ fail(String.format("No log message contains [%s]",
substring));
+ }
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ stop();
+ }
+}
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/U.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java
similarity index 51%
rename from
sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/U.java
rename to
sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java
index 0ab3ff6..85417d7 100644
---
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/U.java
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java
@@ -16,12 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sling.graphql.schema.aggregator.impl;
+package org.apache.sling.graphql.schema.aggregator;
import org.osgi.framework.Bundle;
+
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.bundle;
+
+import org.apache.sling.graphql.schema.aggregator.impl.ProviderBundleTracker;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.tinybundles.core.TinyBundle;
+import org.osgi.framework.Constants;
+
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import java.io.ByteArrayInputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
@@ -29,8 +40,9 @@ import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
-class U {
- static Bundle testBundle(String symbolicName, long id, int nEntries) {
+/** Test Utilities */
+public class U {
+ public static Bundle mockProviderBundle(String symbolicName, long id,
String ... schemaNames) {
final Bundle b = mock(Bundle.class);
when(b.getSymbolicName()).thenReturn(symbolicName);
when(b.getBundleId()).thenReturn(id);
@@ -41,8 +53,8 @@ class U {
when(b.getHeaders()).thenReturn(headers);
final List<String> resources = new ArrayList<>();
- for(int i=1 ; i <= nEntries; i++) {
- String fakeResource = fakePath + "/resource/" + i;
+ for(String name : schemaNames) {
+ String fakeResource = fakePath + "/resource/" + name;
resources.add(fakeResource);
final URL url = mock(URL.class);
when(url.toString()).thenReturn(fakeResource);
@@ -51,4 +63,29 @@ class U {
when(b.getEntryPaths(fakePath)).thenReturn(Collections.enumeration(resources));
return b;
}
+
+ public static Option providerBundleOption(String symbolicName, String ...
schemaNames) {
+ final String schemaPath = symbolicName + "/schemas";
+ final TinyBundle b = bundle()
+ .set(ProviderBundleTracker.SCHEMA_PATH_HEADER, schemaPath)
+ .set(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+ ;
+
+ for(String name : schemaNames) {
+ final String resourcePath = schemaPath + "/" + name + ".txt";
+ final String content = "Fake schema at " + resourcePath;
+ b.add(resourcePath, new ByteArrayInputStream(content.getBytes()));
+ }
+
+ return streamBundle(b.build());
+ }
+
+ public static void assertPartialsFoundInSchema(String output, String ...
partialName) {
+ for(String name : partialName) {
+ final String expected = "DefaultSchemaAggregator.source=" + name;
+ if(!output.contains(expected)) {
+ fail(String.format("Expecting output to contain %s: %s",
expected, output));
+ }
+ }
+ }
}
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialProviderTest.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialProviderTest.java
new file mode 100644
index 0000000..428ac1b
--- /dev/null
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialProviderTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.schema.aggregator.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.junit.Test;
+
+public class BundleEntryPartialProviderTest {
+ @Test
+ public void partialName() throws MalformedURLException {
+ final URL url = new URL("http://stuff/some/path/the.name.txt");
+ assertEquals("the.name",
BundleEntryPartialProvider.getPartialName(url));
+ }
+}
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
index 5dec881..b558058 100644
---
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
@@ -29,6 +29,7 @@ import java.io.StringWriter;
import java.lang.reflect.Field;
import org.apache.commons.io.IOUtils;
+import org.apache.sling.graphql.schema.aggregator.U;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.BundleContext;
@@ -64,17 +65,27 @@ public class DefaultSchemaAggregatorTest {
}
@Test
- public void twoProviders() throws Exception{
+ public void severalProviders() throws Exception{
final StringWriter target = new StringWriter();
- tracker.addingBundle(U.testBundle("A", 1, 4), null);
- tracker.addingBundle(U.testBundle("B", 2, 2), null);
- dsa.aggregate(target, "B/path/2/resource/1", "A/path/1/resource/3");
+ tracker.addingBundle(U.mockProviderBundle("A", 1, "1.txt", "2.z.txt",
"3", "4"), null);
+ tracker.addingBundle(U.mockProviderBundle("B", 2, "B1", "B2.txt"),
null);
+ dsa.aggregate(target, "B1", "B2", "2.z");
assertContainsIgnoreCase("schema aggregated by
DefaultSchemaAggregator", target.toString());
- try(InputStream is =
getClass().getResourceAsStream("/two-providers-output.txt")) {
+ try(InputStream is =
getClass().getResourceAsStream("/several-providers-output.txt")) {
assertNotNull("Expecting test resource to be present", is);
final String expected = IOUtils.toString(is, "UTF-8");
assertEquals(expected, target.toString().trim());
}
}
+
+ @Test
+ public void regexpSelection() throws Exception {
+ final StringWriter target = new StringWriter();
+ tracker.addingBundle(U.mockProviderBundle("A", 1, "a.authoring.1.txt",
"a.authoring.2.txt", "3", "4"), null);
+ tracker.addingBundle(U.mockProviderBundle("B", 2, "B1",
"B.authoring.txt"), null);
+ dsa.aggregate(target, "B1", "/.*\\.authoring.*/");
+ assertContainsIgnoreCase("schema aggregated by
DefaultSchemaAggregator", target.toString());
+ U.assertPartialsFoundInSchema(target.toString(), "a.authoring.1",
"a.authoring.2", "B.authoring", "B1");
+ }
}
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
index bb2a5df..3e02acc 100644
---
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
@@ -19,12 +19,15 @@
package org.apache.sling.graphql.schema.aggregator.impl;
import org.apache.commons.io.IOUtils;
-import org.apache.sling.graphql.schema.aggregator.api.PartialSchemaProvider;
+import org.apache.sling.graphql.schema.aggregator.LogCapture;
+import org.apache.sling.graphql.schema.aggregator.U;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import ch.qos.logback.classic.Level;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -44,7 +47,7 @@ public class ProviderBundleTrackerTest {
@Test
public void addBundle() {
- final Bundle a = U.testBundle("A", ++bundleId, 1);
+ final Bundle a = U.mockProviderBundle("A", ++bundleId, "1");
tracker.addingBundle(a, null);
assertEquals(1, tracker.getSchemaProviders().size());
@@ -55,11 +58,11 @@ public class ProviderBundleTrackerTest {
@Test
public void addAndRemoveBundles() {
- final Bundle a = U.testBundle("A", ++bundleId, 1);
- final Bundle b = U.testBundle("B", ++bundleId, 1);
+ final Bundle a = U.mockProviderBundle("A", ++bundleId,
"1.graphql.txt");
+ final Bundle b = U.mockProviderBundle("B", ++bundleId, "2.txt",
"1.txt");
tracker.addingBundle(a, null);
tracker.addingBundle(b, null);
- assertEquals(2, tracker.getSchemaProviders().size());
+ assertEquals(3, tracker.getSchemaProviders().size());
tracker.removedBundle(b, null, null);
assertEquals(1, tracker.getSchemaProviders().size());
tracker.removedBundle(a, null, null);
@@ -69,8 +72,19 @@ public class ProviderBundleTrackerTest {
}
@Test
+ public void duplicatePartialName() {
+ final LogCapture capture = new
LogCapture(ProviderBundleTracker.class.getName(), true);
+ final Bundle a = U.mockProviderBundle("A", ++bundleId, "TT");
+ final Bundle b = U.mockProviderBundle("B", ++bundleId, "TT.txt",
"another");
+ tracker.addingBundle(a, null);
+ tracker.addingBundle(b, null);
+ capture.assertContains(Level.WARN, "Partial provider with name TT
already present");
+ assertEquals(2, tracker.getSchemaProviders().size());
+ }
+
+ @Test
public void getSectionsContent() throws IOException {
- final Bundle a = U.testBundle("A", ++bundleId, 1);
+ final Bundle a = U.mockProviderBundle("A", ++bundleId, "1");
tracker.addingBundle(a, null);
final PartialSchemaProvider psp =
tracker.getSchemaProviders().values().iterator().next();
assertEquals("Fake section S1 for A(1):A/path/1/resource/1",
IOUtils.toString(psp.getSectionContent("S1")));
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/SchemaAggregatorServletTest.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/SchemaAggregatorServletTest.java
new file mode 100644
index 0000000..aa801e6
--- /dev/null
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/SchemaAggregatorServletTest.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.graphql.schema.aggregator.impl;
+
+import
org.apache.sling.graphql.schema.aggregator.servlet.SchemaAggregatorServlet;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+public class SchemaAggregatorServletTest {
+
+ private void assertMappings(Map<String, String[]> data, String selector,
String expected) {
+ final String [] names = data.get(selector);
+ assertNotNull("Expecting field names for selector " + selector, names);
+ assertEquals(expected, String.join(",", names));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void selectorMappingConfig() throws Exception {
+ final SchemaAggregatorServlet s = new SchemaAggregatorServlet();
+ final SchemaAggregatorServlet.Config cfg =
mock(SchemaAggregatorServlet.Config.class);
+ final String [] cfgMappings = {
+ "\t S1\t :one, two, \t three \t",
+ "selector_2:4,5"
+ };
+ when(cfg.selectors_to_partials_mapping()).thenReturn(cfgMappings);
+ s.activate(null, cfg);
+ final Field f =
s.getClass().getDeclaredField("selectorsToPartialNames");
+ f.setAccessible(true);
+ final Map<String, String[]> actualMappings = (Map<String,
String[]>)f.get(s);
+ assertEquals(2, actualMappings.size());
+ assertMappings(actualMappings, "S1", "one,two,three");
+ assertMappings(actualMappings, "selector_2", "4,5");
+ }
+}
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/it/SchemaAggregatorServletIT.java
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/it/SchemaAggregatorServletIT.java
index 25d9ddc..96eff50 100644
---
a/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/it/SchemaAggregatorServletIT.java
+++
b/sling-org-apache-sling-graphql-schema/src/test/java/org/apache/sling/graphql/schema/aggregator/it/SchemaAggregatorServletIT.java
@@ -24,8 +24,10 @@ import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
+import static org.junit.Assert.fail;
import static
org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+import org.apache.sling.graphql.schema.aggregator.U;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,21 +42,28 @@ public class SchemaAggregatorServletIT extends
SchemaAggregatorTestSupport {
return new Option[]{
baseConfiguration(),
+ U.providerBundleOption("firstProvider", "firstA",
"firstB","secondN"),
+ U.providerBundleOption("secondProvider", "secondA",
"secondB","secondOther"),
+
// The aggregator servlet is disabled by default
factoryConfiguration(AGGREGATOR_SERVLET_CONFIG_PID)
.put("sling.servlet.resourceTypes", "sling/servlet/default")
.put("sling.servlet.extensions", GQL_SCHEMA_EXT)
- .put("sling.servlet.selectors", new String[] { "X", "Y" })
+ .put("sling.servlet.selectors", new String[] { "X", "Y",
"nomappings" })
.put("sling.servlet.methods", new String[] { "GET" })
+ .put("selectors.to.partials.mapping", new String[] {
"X:firstA,secondB", "Y:secondA,firstB,/second.*/" })
.asOption(),
};
}
@Test
- public void servletIsActive() throws Exception {
- // TODO this doesn't actually test the servlet so far
- //assertEquals("Not a schema yet, for providers [X]", getContent("/."
+ GQL_SCHEMA_EXT));
- getContent("/.json");
+ public void basicAggregation() throws Exception {
+ U.assertPartialsFoundInSchema(getContent("/.X." + GQL_SCHEMA_EXT),
"firstA", "secondB");
+ U.assertPartialsFoundInSchema(getContent("/.Y." + GQL_SCHEMA_EXT),
"secondA", "firstB", "secondB","secondOther","secondN");
}
+ @Test
+ public void unmappedSelector() throws Exception {
+ executeRequest("GET", "/.nomappings." + GQL_SCHEMA_EXT, null, null,
null, 400);
+ }
}
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/resources/several-providers-output.txt
b/sling-org-apache-sling-graphql-schema/src/test/resources/several-providers-output.txt
new file mode 100644
index 0000000..2cab28d
--- /dev/null
+++
b/sling-org-apache-sling-graphql-schema/src/test/resources/several-providers-output.txt
@@ -0,0 +1,35 @@
+# Schema aggregated by DefaultSchemaAggregator
+
+query {
+
+# DefaultSchemaAggregator.source=B1
+Fake section query for B(2):B/path/2/resource/B1
+
+# DefaultSchemaAggregator.source=B2
+Fake section query for B(2):B/path/2/resource/B2.txt
+
+# DefaultSchemaAggregator.source=2.z
+Fake section query for A(1):A/path/1/resource/2.z.txt
+
+}
+
+mutation {
+
+# DefaultSchemaAggregator.source=B1
+Fake section mutation for B(2):B/path/2/resource/B1
+
+# DefaultSchemaAggregator.source=B2
+Fake section mutation for B(2):B/path/2/resource/B2.txt
+
+# DefaultSchemaAggregator.source=2.z
+Fake section mutation for A(1):A/path/1/resource/2.z.txt
+
+}
+
+# DefaultSchemaAggregator.source=B1
+Fake body for B(2):B/path/2/resource/B1
+# DefaultSchemaAggregator.source=B2
+Fake body for B(2):B/path/2/resource/B2.txt
+# DefaultSchemaAggregator.source=2.z
+Fake body for A(1):A/path/1/resource/2.z.txt
+# End of Schema aggregated by DefaultSchemaAggregator
\ No newline at end of file
diff --git
a/sling-org-apache-sling-graphql-schema/src/test/resources/two-providers-output.txt
b/sling-org-apache-sling-graphql-schema/src/test/resources/two-providers-output.txt
deleted file mode 100644
index b896741..0000000
---
a/sling-org-apache-sling-graphql-schema/src/test/resources/two-providers-output.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-# Schema aggregated by DefaultSchemaAggregator
-
-query {
-
-# DefaultSchemaAggregator.source=B/path/2/resource/1
-Fake section query for B(2):B/path/2/resource/1
-
-# DefaultSchemaAggregator.source=A/path/1/resource/3
-Fake section query for A(1):A/path/1/resource/3
-
-}
-
-mutation {
-
-# DefaultSchemaAggregator.source=B/path/2/resource/1
-Fake section mutation for B(2):B/path/2/resource/1
-
-# DefaultSchemaAggregator.source=A/path/1/resource/3
-Fake section mutation for A(1):A/path/1/resource/3
-
-}
-
-# DefaultSchemaAggregator.source=B/path/2/resource/1
-Fake body for B(2):B/path/2/resource/1
-# DefaultSchemaAggregator.source=A/path/1/resource/3
-Fake body for A(1):A/path/1/resource/3
-# End of Schema aggregated by DefaultSchemaAggregator
\ No newline at end of file