Support consecutive slashes in RFC 8040 URIs 93/77693/5
authorJakub Morvay <jmorvay@frinx.io>
Mon, 12 Nov 2018 18:14:20 +0000 (19:14 +0100)
committerJakub Morvay <jmorvay@frinx.io>
Mon, 12 Nov 2018 18:30:59 +0000 (19:30 +0100)
Users often assume restconf URLs behave just as HTTP does by squashing
multiple slashes into a single one. Make sure we do not trigger an error
about an empty element in this case.

This deals with URIs in RFC 8040 restconf implementation.

NETCONF-24
Change-Id: I1c3a2085528e4177b09a2d330a6b05bb1b100147
Signed-off-by: Jakub Morvay <jmorvay@frinx.io>
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializerTest.java

index 2009a072c6a48290678a73f3db088805fe4e52c6..89b6fc899b1e387366fc2aacfcb9d72063e62834 100644 (file)
@@ -394,8 +394,12 @@ public final class YangInstanceIdentifierDeserializer {
             checkValid(RestconfConstants.SLASH == currentChar(variables.getOffset(), variables.getData()),
                     "Identifier must start with '/'.", variables.getData(), variables.getOffset());
 
-            // skip slash
-            skipCurrentChar(variables);
+            // skip consecutive slashes, users often assume restconf URLs behave just as HTTP does by squashing
+            // multiple slashes into a single one
+            while (!allCharsConsumed(variables)
+                    && RestconfConstants.SLASH == currentChar(variables.getOffset(), variables.getData())) {
+                skipCurrentChar(variables);
+            }
 
             // check if slash is not also the last char in identifier
             checkValid(!allCharsConsumed(variables), "Identifier cannot end with '/'.",
index 86aa0307b6ba30ed36cf0823bb2ff54ffb3a7689..8ff0572f0ee13875c4cfbf07d85272d749938166 100644 (file)
@@ -223,6 +223,41 @@ public class YangInstanceIdentifierDeserializerTest {
         assertTrue("Empty result expected", Iterables.isEmpty(result));
     }
 
+    /**
+     * Test of deserialization <code>String</code> URI with identifiers separated by multiple slashes to
+     * {@code Iterable<YangInstanceIdentifier.PathArgument>}.
+     */
+    @Test
+    public void deserializeMultipleSlashesTest() {
+        final Iterable<PathArgument> result = YangInstanceIdentifierDeserializer
+                .create(this.schemaContext, "deserializer-test:contA////list-A=40//list-key");
+
+        assertEquals("Result does not contains expected number of path arguments", 4, Iterables.size(result));
+
+        final Iterator<YangInstanceIdentifier.PathArgument> iterator = result.iterator();
+
+        // container
+        assertEquals("Not expected path argument",
+                YangInstanceIdentifier.NodeIdentifier.create(QName.create("deserializer:test", "2016-06-06", "contA")),
+                iterator.next());
+
+        // list
+        final QName list = QName.create("deserializer:test", "2016-06-06", "list-A");
+        assertEquals("Not expected path argument",
+                YangInstanceIdentifier.NodeIdentifier.create(list),
+                iterator.next());
+        assertEquals("Not expected path argument",
+                new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                        list, QName.create(list, "list-key"), 40).toString(),
+                iterator.next().toString());
+
+        // leaf
+        assertEquals("Not expected path argument",
+                new YangInstanceIdentifier.NodeIdentifier(
+                        QName.create("deserializer:test", "2016-06-06", "list-key")),
+                iterator.next());
+    }
+
     /**
      * Negative test when supplied <code>SchemaContext</code> is null. Test is expected to fail with
      * <code>NullPointerException</code>.
@@ -263,34 +298,44 @@ public class YangInstanceIdentifierDeserializerTest {
         YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA/");
     }
 
+    /**
+     * Negative test of validating identifier when there are multiple slashes after container without next identifier.
+     * Test is expected to fail with <code>IllegalArgumentException</code>.
+     */
+    @Test
+    public void validArgIdentifierContainerEndsWithMultipleSlashesNegativeTest() {
+        this.thrown.expect(IllegalArgumentException.class);
+        YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA///");
+    }
+
     /**
      * Negative test of validating identifier when there is a slash after list key values without next identifier. Test
      * is expected to fail with <code>IllegalArgumentException</code>.
      */
     @Test
-    public void validArgIdentifierListEndsWithSlashLNegativeTest() {
+    public void validArgIdentifierListEndsWithSlashNegativeTest() {
         this.thrown.expect(IllegalArgumentException.class);
         YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key=value/");
     }
 
     /**
-     * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
-     * with <code>IllegalArgumentException</code>.
+     * Negative test of validating identifier when there are multiple slashes after list key values without next
+     * identifier. Test is expected to fail with <code>IllegalArgumentException</code>.
      */
     @Test
-    public void prepareQnameEmptyIdentifierNegativeTest() {
+    public void validArgIdentifierListEndsWithSlashesNegativeTest() {
         this.thrown.expect(IllegalArgumentException.class);
-        YangInstanceIdentifierDeserializer.create(this.schemaContext, "/");
+        YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:list-one-key=value//");
     }
 
     /**
-     * Negative test of creating <code>QName</code> when two identifiers are separated by two slashes. Test is
-     * expected to fail with <code>IllegalArgumentException</code>.
+     * Negative test of creating <code>QName</code> when identifier is empty (example: '/'). Test is expected to fail
+     * with <code>IllegalArgumentException</code>.
      */
     @Test
-    public void prepareQnameTwoSlashesNegativeTest() {
+    public void prepareQnameEmptyIdentifierNegativeTest() {
         this.thrown.expect(IllegalArgumentException.class);
-        YangInstanceIdentifierDeserializer.create(this.schemaContext, "deserializer-test:contA//leaf-A");
+        YangInstanceIdentifierDeserializer.create(this.schemaContext, "/");
     }
 
     /**