You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2021/07/28 15:46:39 UTC

[sling-org-apache-sling-graphql-schema-aggregator] branch issue/SLING-10681 created (now 6947dd9)

This is an automated email from the ASF dual-hosted git repository.

radu pushed a change to branch issue/SLING-10681
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-schema-aggregator.git.


      at 6947dd9  SLING-10681 - Restrict the naming pattern for partials

This branch includes the following new commits:

     new 940d300  SLING-10681 - Restrict the naming pattern for partials
     new 6947dd9  SLING-10681 - Restrict the naming pattern for partials

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[sling-org-apache-sling-graphql-schema-aggregator] 01/02: SLING-10681 - Restrict the naming pattern for partials

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch issue/SLING-10681
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-schema-aggregator.git

commit 940d3000ef30144fd3e58fef7970771dc21e0efc
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Wed Jul 28 17:21:53 2021 +0200

    SLING-10681 - Restrict the naming pattern for partials
    
    * renamed test partials to align them with the upcoming partial names
    pattern restriction
---
 .../impl/DefaultSchemaAggregatorTest.java          | 26 +++++++++++-----------
 .../schema/aggregator/impl/PartialReaderTest.java  |  2 +-
 .../aggregator/impl/ProviderBundleTrackerTest.java | 18 +++++++--------
 .../partials/several-providers-output.txt          | 14 ++++++------
 4 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
index cad4510..c0268eb 100644
--- a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
+++ b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/DefaultSchemaAggregatorTest.java
@@ -52,7 +52,7 @@ public class DefaultSchemaAggregatorTest {
     private void assertOutput(String expectedResourceName, String actual) throws IOException {
         try(InputStream is = getClass().getResourceAsStream(expectedResourceName)) {
             assertNotNull("Expecting classpath resource to be present:" + expectedResourceName, is);
-            final String expected = IOUtils.toString(is, "UTF-8");
+            final String expected = IOUtils.toString(is, "UTF-8").trim();
             assertEquals(expected, actual);
         }
     }
@@ -86,9 +86,9 @@ public class DefaultSchemaAggregatorTest {
     @Test
     public void severalProviders() throws Exception{
         final StringWriter target = new StringWriter();
-        tracker.addingBundle(U.mockProviderBundle(bundleContext, "A", 1, "1.txt", "2.z.w", "3abc", "4abc"), null);
-        tracker.addingBundle(U.mockProviderBundle(bundleContext, "B", 2, "B1a.txt", "B2.xy"), null);
-        dsa.aggregate(target, "B1a", "B2", "2.z");
+        tracker.addingBundle(U.mockProviderBundle(bundleContext, "A", 1, "a1.txt", "a2.z.w.txt", "a3abc.txt", "a4abc.txt"), null);
+        tracker.addingBundle(U.mockProviderBundle(bundleContext, "B", 2, "b1a.txt", "b2.xy.txt"), null);
+        dsa.aggregate(target, "b1a", "b2.xy", "a2.z.w");
         final String sdl = target.toString().trim();
         assertContainsIgnoreCase("schema aggregated by DefaultSchemaAggregator", sdl);
         assertOutput("/partials/several-providers-output.txt", sdl);
@@ -97,11 +97,11 @@ public class DefaultSchemaAggregatorTest {
     @Test
     public void regexpSelection() throws Exception {
         final StringWriter target = new StringWriter();
-        tracker.addingBundle(U.mockProviderBundle(bundleContext, "A", 1, "a.authoring.1.txt", "a.authoring.2.txt", "3.txt", "4.txt"), null);
-        tracker.addingBundle(U.mockProviderBundle(bundleContext, "B", 2, "B1.txt", "B.authoring.txt"), null);
-        dsa.aggregate(target, "B1", "/.*\\.authoring.*/");
+        tracker.addingBundle(U.mockProviderBundle(bundleContext, "A", 1, "a.authoring.1.txt", "a.authoring.2.txt", "a.txt", "b.txt"), null);
+        tracker.addingBundle(U.mockProviderBundle(bundleContext, "B", 2, "b1.txt", "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");
+        U.assertPartialsFoundInSchema(target.toString(), "a.authoring.1", "a.authoring.2", "b.authoring", "b1");
     }
 
     @Test
@@ -191,15 +191,15 @@ public class DefaultSchemaAggregatorTest {
     @Test
     public void providersOrdering() throws Exception {
         final StringWriter target = new StringWriter();
-        tracker.addingBundle(U.mockProviderBundle(bundleContext, "ordering", 1, "Aprov.txt", "Cprov.txt", "Z_test.txt", "A_test.txt",
-                "Zprov.txt",
-                "Z_test.txt", "Bprov.txt", "C_test.txt"), null);
-        dsa.aggregate(target, "Aprov", "Zprov", "/[A-Z]_test/", "A_test", "Cprov");
+        tracker.addingBundle(U.mockProviderBundle(bundleContext, "ordering", 1, "aprov.txt", "cprov.txt", "z_test.txt", "a_test.txt",
+                "zprov.txt",
+                "z_test.txt", "bprov.txt", "c_test.txt"), null);
+        dsa.aggregate(target, "aprov", "zprov", "/[a-z]_test/", "a_test", "cprov");
         final String sdl = target.toString();
 
         // The order of named partials is kept, regexp selected ones are ordered by name
         // And A_test has already been used so it's not used again when called explicitly after regexp
-        final String expected = "End of Schema aggregated from {Aprov,Zprov,A_test,C_test,Z_test,Cprov} by DefaultSchemaAggregator";
+        final String expected = "End of Schema aggregated from {aprov,zprov,a_test,c_test,z_test,cprov} by DefaultSchemaAggregator";
         assertTrue(String.format("Expecting schema to contain [%s]: %s", expected, sdl), sdl.contains(expected));
    }
 }
diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java
index a7863cf..6fca4d1 100644
--- a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java
+++ b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java
@@ -51,7 +51,7 @@ public class PartialReaderTest {
         }
         if(contentRegexp != null) {
             try(Reader r = s.getContent()) {
-                final String actual = IOUtils.toString(s.getContent()).trim();
+                final String actual = IOUtils.toString(r).trim();
                 final Pattern regexp = Pattern.compile(contentRegexp, Pattern.DOTALL);
                 assertTrue(
                     String.format("Expecting section %s to match %s but was [%s]", name, contentRegexp, actual),
diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
index 1f78d9b..2ceee73 100644
--- a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
+++ b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/ProviderBundleTrackerTest.java
@@ -52,19 +52,19 @@ public class ProviderBundleTrackerTest {
 
     @Test
     public void addBundle() throws Exception {
-        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "1.txt");
+        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "a.txt");
         tracker.addingBundle(a, null);
         assertEquals(1, tracker.getSchemaProviders().size());
 
         final Partial s = tracker.getSchemaProviders().values().iterator().next();
         assertTrue(s.toString().contains(a.getSymbolicName()));
-        assertTrue(s.toString().contains("1.txt"));
+        assertTrue(s.toString().contains("a.txt"));
     }
 
     @Test
     public void addAndRemoveBundles() throws Exception {
-        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "1.graphql.txt");
-        final Bundle b = U.mockProviderBundle(bundleContext, "B", ++bundleId, "2.txt", "1.txt");
+        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "a1.graphql.txt");
+        final Bundle b = U.mockProviderBundle(bundleContext, "B", ++bundleId, "b2.txt", "b1.txt");
         tracker.addingBundle(a, null);
         tracker.addingBundle(b, null);
         assertEquals(3, tracker.getSchemaProviders().size());
@@ -79,11 +79,11 @@ public class ProviderBundleTrackerTest {
     @Test
     public void duplicatePartialName() throws Exception {
         final LogCapture capture = new LogCapture(ProviderBundleTracker.class.getName(), true);
-        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "TT.txt");
-        final Bundle b = U.mockProviderBundle(bundleContext, "B", ++bundleId, "TT.txt", "another.x");
+        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "tt.txt");
+        final Bundle b = U.mockProviderBundle(bundleContext, "B", ++bundleId, "tt.txt", "another.x");
         tracker.addingBundle(a, null);
         tracker.addingBundle(b, null);
-        capture.assertContains(Level.WARN, "Partial provider with name TT already present");
+        capture.assertContains(Level.WARN, "Partial provider with name tt already present");
         assertEquals(2, tracker.getSchemaProviders().size());
     }
 
@@ -95,9 +95,9 @@ public class ProviderBundleTrackerTest {
  
     @Test
     public void getSectionsContent() throws IOException {
-        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "1.txt");
+        final Bundle a = U.mockProviderBundle(bundleContext, "A", ++bundleId, "a1.txt");
         tracker.addingBundle(a, null);
         final Partial p = tracker.getSchemaProviders().values().iterator().next();
-        assertSectionContent(p, Partial.SectionName.QUERY, "Fake query for 1.txt");
+        assertSectionContent(p, Partial.SectionName.QUERY, "Fake query for a1.txt");
     }
 }
diff --git a/src/test/resources/partials/several-providers-output.txt b/src/test/resources/partials/several-providers-output.txt
index b3fa1f7..cff4dc8 100644
--- a/src/test/resources/partials/several-providers-output.txt
+++ b/src/test/resources/partials/several-providers-output.txt
@@ -2,15 +2,15 @@
 
 type Query {
 
-# DefaultSchemaAggregator.source=B1a
-Fake query for B1a.txt
+# DefaultSchemaAggregator.source=b1a
+Fake query for b1a.txt
 
-# DefaultSchemaAggregator.source=B2
-Fake query for B2.xy
+# DefaultSchemaAggregator.source=b2.xy
+Fake query for b2.xy.txt
 
-# DefaultSchemaAggregator.source=2.z
-Fake query for 2.z.w
+# DefaultSchemaAggregator.source=a2.z.w
+Fake query for a2.z.w.txt
 
 }
 
-# End of Schema aggregated from {B1a,B2,2.z} by DefaultSchemaAggregator
\ No newline at end of file
+# End of Schema aggregated from {b1a,b2.xy,a2.z.w} by DefaultSchemaAggregator

[sling-org-apache-sling-graphql-schema-aggregator] 02/02: SLING-10681 - Restrict the naming pattern for partials

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch issue/SLING-10681
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-schema-aggregator.git

commit 6947dd9bc93d807cec573e3cc126573ee5a4111a
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Wed Jul 28 17:44:00 2021 +0200

    SLING-10681 - Restrict the naming pattern for partials
    
    * added a naming pattern for partials and provided classes to help with
    reading/validating partials
---
 .../schema/aggregator/impl/BundleEntryPartial.java |  14 +-
 .../graphql/schema/aggregator/impl/Partial.java    |  30 ++++-
 .../schema/aggregator/impl/PartialInfo.java        | 142 +++++++++++++++++++++
 .../schema/aggregator/impl/PartialReader.java      |  15 ++-
 .../apache/sling/graphql/schema/aggregator/U.java  |  12 +-
 .../aggregator/impl/BundleEntryPartialTest.java    |  34 -----
 .../schema/aggregator/impl/PartialInfoTest.java    |  88 +++++++++++++
 .../schema/aggregator/impl/PartialReaderTest.java  |  28 +++-
 8 files changed, 295 insertions(+), 68 deletions(-)

diff --git a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartial.java b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartial.java
index 8eb1ad2..3207e0a 100644
--- a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartial.java
+++ b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartial.java
@@ -33,17 +33,9 @@ class BundleEntryPartial extends PartialReader implements Comparable<BundleEntry
     private final long bundleId;
 
     private BundleEntryPartial(Bundle b, URL bundleEntry) throws IOException {
-        super(getPartialName(bundleEntry), new URLReaderSupplier(bundleEntry));
+        super(PartialInfo.fromURL(bundleEntry), new URLReaderSupplier(bundleEntry));
         this.bundleId = b.getBundleId();
-        this.key = String.format("%s(%d):%s", b.getSymbolicName(), b.getBundleId(), bundleEntry.toString());
-    }
-
-    /** 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;
+        this.key = String.format("%s(%d):%s", b.getSymbolicName(), b.getBundleId(), bundleEntry);
     }
 
     /** @return a BundleEntryPartialProvider for the entryPath in
@@ -84,4 +76,4 @@ class BundleEntryPartial extends PartialReader implements Comparable<BundleEntry
     public int compareTo(BundleEntryPartial o) {
         return getName().compareTo(o.getName());
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/Partial.java b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/Partial.java
index 21f02e5..e167c65 100644
--- a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/Partial.java
+++ b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/Partial.java
@@ -23,12 +23,15 @@ import java.io.Reader;
 import java.util.Optional;
 import java.util.Set;
 
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.Version;
+
 /** Wrapper for the partials format, that parses a partial file and
  *  provides access to its sections.
  *  See the example.partial.txt and the tests for a description of
  *  the format.
   */
-interface Partial {
+public interface Partial {
     /** A section in the partial */
     interface Section {
         SectionName getName();
@@ -43,14 +46,29 @@ interface Partial {
         QUERY,
         MUTATION,
         TYPES
-    };
+    }
 
     /** The name of this partial */
-    String getName();
+    @NotNull String getName();
 
     /** Return a specific section of the partial, by name */
-    Optional<Section> getSection(SectionName name);
+    @NotNull Optional<Section> getSection(SectionName name);
 
     /** Names of the Partials on which this one depends */
-    Set<String> getRequiredPartialNames();
-}
\ No newline at end of file
+    @NotNull Set<String> getRequiredPartialNames();
+
+    /**
+     * Returns the MD5 hash of the source that was used to build this partial.
+     *
+     * @return the MD5 hash of the source that was used to build this partial
+     */
+    @NotNull String getMD5Hash();
+
+    /**
+     * Returns the version of this partial.
+     * @return the version of this partial.
+     */
+    @NotNull default Version getVersion() {
+        return Version.emptyVersion;
+    }
+}
diff --git a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialInfo.java b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialInfo.java
new file mode 100644
index 0000000..6b18bee
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialInfo.java
@@ -0,0 +1,142 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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 java.net.URL;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.Version;
+
+/**
+ * This class provides some utility methods to extract information about a partial without parsing it.
+ */
+public final class PartialInfo {
+
+    private static final String PARTIAL_NAME_AND_VERSION_REGEX = "([a-z][a-zA-Z0-9_\\.]*)(-(\\d\\.\\d\\.\\d))?";
+
+    public static final String PARTIAL_FILE_EXTENSION = "txt";
+    public static final Pattern PARTIAL_FILE_NAME_PATTERN =
+            Pattern.compile(PARTIAL_NAME_AND_VERSION_REGEX + "\\." + PARTIAL_FILE_EXTENSION);
+    public static final Pattern PARTIAL_REQUIRE_HEADER_PATTERN = Pattern.compile(PARTIAL_NAME_AND_VERSION_REGEX);
+    public static final int PARTIAL_NAME_GROUP = 1;
+    public static final int PARTIAL_VERSION_GROUP = 3;
+
+
+    private final String name;
+    private final Version version;
+
+    public static final PartialInfo EMPTY = new PartialInfo("", Version.emptyVersion);
+
+    PartialInfo(@NotNull String name, @NotNull Version version) {
+        this.name = name;
+        this.version = version;
+    }
+
+    /**
+     * Returns the partial's name.
+     *
+     * @return the partial's name
+     */
+    public @NotNull String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the partial's version.
+     *
+     * @return the partial's version
+     */
+    public @NotNull Version getVersion() {
+        return version;
+    }
+
+    /**
+     * Parses a {@code path} and returns a {@link PartialInfo}.
+     *
+     * @param path the path to parse
+     * @return a {@link PartialInfo} with the parsed details; a {@link PartialInfo#EMPTY} means that the parsing was not able to identify a
+     * valid {@link PartialInfo}
+     */
+    public static @NotNull PartialInfo fromPath(@NotNull Path path) {
+        return fromFileName(path.getFileName().toString());
+    }
+
+    /**
+     * Parses a {@code url} and returns a {@link PartialInfo}.
+     *
+     * @param url the url to parse
+     * @return a {@link PartialInfo} with the parsed details; a {@link PartialInfo#EMPTY} means that the parsing was not able to identify a
+     * valid {@link PartialInfo}
+     */
+    public static @NotNull PartialInfo fromURL(@NotNull URL url) {
+        String file = url.getFile();
+        if (!file.isEmpty()) {
+            int lastSlash = file.lastIndexOf('/');
+            if (lastSlash > -1 && lastSlash != file.length() - 1) {
+                String fileName = file.substring(lastSlash + 1);
+                return fromFileName(fileName);
+            }
+        }
+        return PartialInfo.EMPTY;
+    }
+
+    /**
+     * Parses a {@code fileName} and returns a {@link PartialInfo}.
+     *
+     * @param fileName the file name to parse
+     * @return a {@link PartialInfo} with the parsed details; a {@link PartialInfo#EMPTY} means that the parsing was not able to identify a
+     * valid {@link PartialInfo}
+     */
+    public static @NotNull PartialInfo fromFileName(@NotNull String fileName) {
+        Matcher matcher = PARTIAL_FILE_NAME_PATTERN.matcher(fileName);
+        if (matcher.matches()) {
+            return new PartialInfo(matcher.group(PARTIAL_NAME_GROUP),
+                    Version.parseVersion(matcher.group(PARTIAL_VERSION_GROUP)));
+        }
+        return PartialInfo.EMPTY;
+    }
+
+    /**
+     * Parses the partial names provided in a {@code REQUIRES} section of a {@link Partial}.
+     *
+     * @param requires the value of the {@code REQUIRES} section
+     * @return a set of {@link PartialInfo}
+     */
+    public static @NotNull Set<PartialInfo> fromRequiresSection(@NotNull String requires) {
+        if (!requires.isEmpty()) {
+            Set<PartialInfo> partialInfos = new HashSet<>();
+            for (String partial : requires.split(",")) {
+                Matcher matcher = PARTIAL_REQUIRE_HEADER_PATTERN.matcher(partial.trim());
+                if (matcher.matches()) {
+                    partialInfos.add(new PartialInfo(matcher.group(PARTIAL_NAME_GROUP),
+                            Version.parseVersion(matcher.group(PARTIAL_VERSION_GROUP))));
+                }
+            }
+            return Collections.unmodifiableSet(partialInfos);
+        }
+        return Collections.emptySet();
+    }
+}
+
diff --git a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReader.java b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReader.java
index b7fcea0..54f13e5 100644
--- a/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReader.java
+++ b/src/main/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReader.java
@@ -32,13 +32,14 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.commons.io.input.BoundedReader;
+import org.jetbrains.annotations.NotNull;
 
 /** Reader for the partials format, which parses a partial file and
  *  provides access to its sections.
  *  See the example.partial.txt and the tests for a description of
  *  the format.
   */
-class PartialReader implements Partial {
+public class PartialReader implements Partial {
     private static final Pattern SECTION_LINE = Pattern.compile("([A-Z]+) *:(.*)");
     private static final int EOL = '\n';
 
@@ -88,8 +89,8 @@ class PartialReader implements Partial {
         }
     }
     
-    PartialReader(String name, Supplier<Reader> source) throws IOException {
-        this.name = name;
+    public PartialReader(@NotNull PartialInfo partialInfo, @NotNull Supplier<Reader> source) throws IOException {
+        this.name = partialInfo.getName();
         parse(source);
         final Partial.Section requirements = sections.get(SectionName.REQUIRES);
         if(requirements == null) {
@@ -167,18 +168,18 @@ class PartialReader implements Partial {
     }
 
     @Override
-    public Optional<Section> getSection(Partial.SectionName name) {
+    public @NotNull Optional<Section> getSection(Partial.SectionName name) {
         final Section s = sections.get(name);
-        return s == null ? Optional.empty() : Optional.of(s);
+        return Optional.ofNullable(s);
     }
 
     @Override
-    public String getName() {
+    public @NotNull String getName() {
         return name;
     }
 
     @Override
-    public Set<String> getRequiredPartialNames() {
+    public @NotNull Set<String> getRequiredPartialNames() {
         return requiredPartialNames;
     }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java
index 72fea60..cbf3451 100644
--- a/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java
+++ b/src/test/java/org/apache/sling/graphql/schema/aggregator/U.java
@@ -43,17 +43,22 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileAttribute;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.UUID;
 
 /** Test Utilities */
 public class U {
     
     public static Bundle mockProviderBundle(BundleContext bc, String symbolicName, long id, String ... schemaNames) throws IOException {
+        final UUID uuid = UUID.randomUUID();
         final Bundle b = mock(Bundle.class);
         final BundleWiring wiring = mock(BundleWiring.class);
         when(b.getSymbolicName()).thenReturn(symbolicName);
@@ -74,7 +79,8 @@ public class U {
         for(String name : schemaNames) {
             URL partial = testFileURL(name);
             if(partial == null) {
-                partial = fakePartialURL(name);
+                File tempFolder = Files.createTempDirectory(uuid.toString()).toFile();
+                partial = fakePartialURL(tempFolder, name);
             }
             String fakeResource = fakePath + "/resource/" + name;
             resources.add(fakeResource);
@@ -85,8 +91,8 @@ public class U {
     }
 
     /** Simple way to get a URL: create a temp file */
-    public static URL fakePartialURL(String name) throws IOException {
-        final File f = File.createTempFile(name, "txt");
+    public static URL fakePartialURL(File folder, String name) throws IOException {
+        final File f = new File(folder, name);
         f.deleteOnExit();
         final PrintWriter w = new PrintWriter(new FileWriter(f));
         w.print(fakePartialSchema(name));
diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialTest.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialTest.java
deleted file mode 100644
index 046281c..0000000
--- a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/BundleEntryPartialTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 BundleEntryPartialTest {
-    @Test
-    public void partialName() throws MalformedURLException {
-        final URL url = new URL("http://stuff/some/path/the.name.txt");
-        assertEquals("the.name", BundleEntryPartial.getPartialName(url));
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialInfoTest.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialInfoTest.java
new file mode 100644
index 0000000..d2a3be3
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialInfoTest.java
@@ -0,0 +1,88 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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 java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Paths;
+import java.util.Set;
+
+import org.junit.Test;
+import org.osgi.framework.Version;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PartialInfoTest {
+
+    @Test
+    public void testFileNameParsing() {
+        PartialInfo p1 = PartialInfo.fromFileName("partial.txt");
+        assertEquals("partial", p1.getName());
+        assertEquals(Version.emptyVersion, p1.getVersion());
+
+        PartialInfo p2 = PartialInfo.fromFileName("partial-2.0.0.txt");
+        assertEquals("partial", p2.getName());
+        assertEquals(Version.parseVersion("2.0.0"), p2.getVersion());
+
+        assertEquals(PartialInfo.EMPTY, PartialInfo.fromFileName("partial-a.b.c.txt"));
+        assertEquals(PartialInfo.EMPTY, PartialInfo.fromFileName("1.2.3"));
+    }
+
+    @Test
+    public void testPathParsing() {
+        PartialInfo p1 = PartialInfo.fromPath(Paths.get("a", "path", "section", "partial.txt"));
+        assertEquals("partial", p1.getName());
+        assertEquals(Version.emptyVersion, p1.getVersion());
+
+        PartialInfo p2 = PartialInfo.fromPath(Paths.get("partial-2.0.0.txt"));
+        assertEquals("partial", p2.getName());
+        assertEquals(Version.parseVersion("2.0.0"), p2.getVersion());
+
+        assertEquals(PartialInfo.EMPTY, PartialInfo.fromPath(Paths.get("partial-a.b.c.txt")));
+        assertEquals(PartialInfo.EMPTY, PartialInfo.fromPath(Paths.get("1.2.3")));
+    }
+
+    @Test
+    public void testURLParsing() throws MalformedURLException {
+        PartialInfo p1 = PartialInfo.fromURL(new URL( "file:///root/partial.txt"));
+        assertEquals("partial", p1.getName());
+        assertEquals(Version.emptyVersion, p1.getVersion());
+
+        PartialInfo p2 = PartialInfo.fromURL(new URL("file:///partial-2.0.0.txt"));
+        assertEquals("partial", p2.getName());
+        assertEquals(Version.parseVersion("2.0.0"), p2.getVersion());
+
+        assertEquals(PartialInfo.EMPTY, PartialInfo.fromURL(new URL("file:///bid/folder/partial-a.b.c.txt")));
+        assertEquals(PartialInfo.EMPTY, PartialInfo.fromURL(new URL("file:///bid/folder/1.2.3")));
+    }
+
+    @Test
+    public void testFromRequireHeader() {
+        Set<PartialInfo> parsed = PartialInfo.fromRequiresSection("partial, a_partial-1.0.0, 0");
+        assertEquals(2, parsed.size());
+        for (PartialInfo partialInfo : parsed) {
+            assertTrue(
+                    "partial".equals(partialInfo.getName()) ||
+                            ("a_partial".equals(partialInfo.getName()) && Version.parseVersion("1.0.0").equals(partialInfo.getVersion()))
+            );
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java
index 6fca4d1..6e031f5 100644
--- a/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java
+++ b/src/test/java/org/apache/sling/graphql/schema/aggregator/impl/PartialReaderTest.java
@@ -30,6 +30,7 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Paths;
 import java.util.Optional;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
@@ -79,7 +80,10 @@ public class PartialReaderTest {
 
     @Test
     public void parseExample() throws Exception {
-        final PartialReader p = new PartialReader(NONAME, getResourceReaderSupplier("/partials/example.partial.txt"));
+        final PartialReader p = new PartialReader(
+                PartialInfo.fromPath(Paths.get("/partials/example.partial.txt")),
+                getResourceReaderSupplier("/partials/example.partial.txt")
+        );
         assertSection(p, "PARTIAL", "Example GraphQL schema partial", "The contents.*PARTIAL.*PARTIAL.*PARTIAL.*equired section\\.");
         assertSection(p, "REQUIRES", "base.scalars, base.schema", null);
         assertSection(p, "PROLOGUE", "", "The prologue content.*the aggregated schema.*other sections\\.");
@@ -90,7 +94,10 @@ public class PartialReaderTest {
 
     @Test
     public void accentedCharacters() throws Exception {
-        final PartialReader p = new PartialReader(NONAME, getResourceReaderSupplier("/partials/utf8.partial.txt"));
+        final PartialReader p = new PartialReader(
+                PartialInfo.fromPath(Paths.get("/partials/utf8.partial.txt")),
+                getResourceReaderSupplier("/partials/utf8.partial.txt")
+        );
         assertSection(p, "PARTIAL", 
             "Example GraphQL schema partial with caract\u00E8res accentu\u00E9s",
             "L'\u00E9t\u00E9 nous \u00E9vitons l'\u00E2tre et pr\u00E9f\u00E9rons Chateaun\u00F6f et les \u00E4kr\u00E0s."
@@ -101,7 +108,7 @@ public class PartialReaderTest {
     public void missingPartialSection() throws Exception {
         final Exception e = assertThrows(
             PartialReader.SyntaxException.class, 
-            () -> new PartialReader(NONAME, getStringReaderSupplier(""))
+            () -> new PartialReader(PartialInfo.EMPTY, getStringReaderSupplier(""))
         );
         final String expected = "Missing required PARTIAL section";
         assertTrue(String.format("Expected %s in %s", expected, e.getMessage()), e.getMessage().contains(expected));
@@ -112,7 +119,8 @@ public class PartialReaderTest {
         final String invalidName = "REQUIRE";
         final Exception e = assertThrows(
             PartialReader.SyntaxException.class, 
-            () -> new PartialReader(NONAME, getStringReaderSupplier(String.format("PARTIAL:test\n%s:something\n", invalidName)))
+            () -> new PartialReader(
+                    PartialInfo.EMPTY, getStringReaderSupplier(String.format("PARTIAL:test\n%s:something\n", invalidName)))
         );
         final String expected = "Invalid section name 'REQUIRE'";
         assertTrue(String.format("Expected %s in %s", expected, e.getMessage()), e.getMessage().contains(expected));
@@ -122,7 +130,10 @@ public class PartialReaderTest {
     public void duplicateSection() throws Exception {
         final Exception e = assertThrows(
             PartialReader.SyntaxException.class, 
-            () -> new PartialReader(NONAME, getResourceReaderSupplier("/partials/duplicate.section.partial.txt"))
+            () -> new PartialReader(
+                    PartialInfo.fromPath(Paths.get("/partials/duplicate.section.partial.txt")),
+                    getResourceReaderSupplier("/partials/duplicate.section.partial.txt")
+            )
         );
         final String expected = "Duplicate section 'QUERY'";
         assertTrue(String.format("Expected %s in %s", expected, e.getMessage()), e.getMessage().contains(expected));
@@ -130,8 +141,11 @@ public class PartialReaderTest {
 
     @Test
     public void requires() throws Exception {
-        final PartialReader p = new PartialReader(NONAME, getResourceReaderSupplier("/partials/c.sdl.txt"));
+        final PartialReader p = new PartialReader(
+                PartialInfo.fromPath(Paths.get("/partials/c.sdl.txt")),
+                getResourceReaderSupplier("/partials/c.sdl.txt")
+        );
         assertTrue("Expecting requires section", p.getSection(Partial.SectionName.REQUIRES).isPresent());
         assertEquals("[a.sdl, b.sdl]", p.getRequiredPartialNames().toString());
     }
-}
\ No newline at end of file
+}