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

Reply via email to