Fix YANG patch request for augmented element 12/104712/2
authorOleksandrZharov <Oleksandr.Zharov@pantheon.tech>
Fri, 26 Aug 2022 15:43:24 +0000 (17:43 +0200)
committerRobert Varga <nite@hq.sk>
Mon, 20 Mar 2023 09:20:06 +0000 (09:20 +0000)
When writing JSON data using yangtool's NormalizedNodeStreamWriter
we have to check if the result is instance of Augmentation.

In that case we have to use its child as data. Similar process
is applied in JsonNormalizedNodeBodyReader.

JIRA: NETCONF-747
Change-Id: I2c1d1abcd3cbb483b414408c2c768bdc5731b8ce
Signed-off-by: OleksandrZharov <Oleksandr.Zharov@pantheon.tech>
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
Signed-off-by: Yaroslav Lastivka <yaroslav.lastivka@pantheon.tech>
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/patch/JsonPatchBodyReader.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/patch/JsonPatchBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/patch/XmlPatchBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/test/AbstractBodyReaderTest.java
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/test-model-aug.yang [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/test-model.yang [new file with mode: 0644]

index c581243ab3073cc3785fb39e38922d950aa03c75..f04d763a23bd9d31206ffcee1a8322175896872e 100644 (file)
@@ -42,8 +42,8 @@ import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactorySupplier;
 import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
@@ -374,12 +374,20 @@ public class JsonPatchBodyReader extends AbstractPatchBodyReader {
      */
     private static NormalizedNode readEditData(final @NonNull JsonReader in,
              final @NonNull Inference targetSchemaNode, final @NonNull InstanceIdentifierContext path) {
-        final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
-        final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
+        final var resultHolder = new NormalizedNodeResult();
+        final var writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
         JsonParserStream.create(writer, JSONCodecFactorySupplier.RFC7951.getShared(path.getSchemaContext()),
             targetSchemaNode).parse(in);
 
-        return resultHolder.getResult();
+        // In case AugmentationNode additional step to get actual data node is required
+        var data = resultHolder.getResult();
+        while (data instanceof AugmentationNode) {
+            final var augNode = (AugmentationNode) data;
+            final var it = augNode.body().iterator();
+            verify(it.hasNext(), "Augmentation %s is missing child", data);
+            data = it.next();
+        }
+        return data;
     }
 
     /**
index d9f42d9978f6247a0f53a38c25545f5c8ba58551..6f4b1cbc7d0be6fb2df6adf86bb5a3020b7f41aa 100644 (file)
@@ -11,7 +11,9 @@ import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 
+import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -20,6 +22,9 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.AbstractBodyReaderTest;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.JsonBodyReaderTest;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public class JsonPatchBodyReaderTest extends AbstractBodyReaderTest {
@@ -177,4 +182,39 @@ public class JsonPatchBodyReaderTest extends AbstractBodyReaderTest {
         final PatchContext returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
         checkPatchContext(returnValue);
     }
+
+    /**
+     * Test of Yang Patch on the top augmented element.
+     */
+    @Test
+    public void modulePatchTargetTopLevelAugmentedContainerTest() throws Exception {
+        mockBodyReader("", jsonToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream(("{\n"
+            + "    \"ietf-yang-patch:yang-patch\": {\n"
+            + "        \"patch-id\": \"test-patch\",\n"
+            + "        \"comment\": \"comment\",\n"
+            + "        \"edit\": [\n"
+            + "            {\n"
+            + "                \"edit-id\": \"edit1\",\n"
+            + "                \"operation\": \"replace\",\n"
+            + "                \"target\": \"/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug\",\n"
+            + "                \"value\": {\n"
+            + "                    \"container-aug\": {\n"
+            + "                        \"leaf-aug\": \"data\"\n"
+            + "                    }\n"
+            + "                }\n"
+            + "            }\n"
+            + "        ]\n"
+            + "    }\n"
+            + "}").getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
+                .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
+                .build();
+        final var returnValue = jsonToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(CONT_AUG_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
 }
index 96e16d71ebbffa2df60f84ad5975d38d8acc6bef..439683b18ed1739329fbd79efc3b42507eb9568a 100644 (file)
@@ -10,7 +10,9 @@ package org.opendaylight.restconf.nb.rfc8040.jersey.providers.patch;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThrows;
 
+import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -18,6 +20,9 @@ import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.AbstractBodyReaderTest;
 import org.opendaylight.restconf.nb.rfc8040.jersey.providers.test.XmlBodyReaderTest;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public class XmlPatchBodyReaderTest extends AbstractBodyReaderTest {
@@ -127,4 +132,36 @@ public class XmlPatchBodyReaderTest extends AbstractBodyReaderTest {
                 .getResourceAsStream("/instanceidentifier/xml/xmlPATCHTargetTopLevelContainerWithEmptyURI.xml");
         checkPatchContext(xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream));
     }
+
+    /**
+     * Test of Yang Patch on the top augmented element.
+     */
+    @Test
+    public void moduleTargetTopLevelAugmentedContainerTest() throws Exception {
+        mockBodyReader("", xmlToPatchBodyReader, false);
+        final var inputStream = new ByteArrayInputStream((
+            "<yang-patch xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-patch\">\n"
+                + "    <patch-id>test-patch</patch-id>\n"
+                + "    <comment>This test patch for augmented element</comment>\n"
+                + "    <edit>\n"
+                + "        <edit-id>edit1</edit-id>\n"
+                + "        <operation>replace</operation>\n"
+                + "        <target>/test-m:container-root/test-m:container-lvl1/test-m-aug:container-aug</target>\n"
+                + "        <value>\n"
+                + "            <container-aug xmlns=\"test-ns-aug\">\n"
+                + "                <leaf-aug>data</leaf-aug>\n"
+                + "            </container-aug>\n"
+                + "        </value>\n"
+                + "    </edit>\n"
+                + "</yang-patch>").getBytes(StandardCharsets.UTF_8));
+        final var expectedData = Builders.containerBuilder()
+                .withNodeIdentifier(new NodeIdentifier(CONT_AUG_QNAME))
+                .withChild(ImmutableNodes.leafNode(LEAF_AUG_QNAME, "data"))
+                .build();
+        final var returnValue = xmlToPatchBodyReader.readFrom(null, null, null, mediaType, null, inputStream);
+        checkPatchContext(returnValue);
+        final var data = returnValue.getData().get(0).getNode();
+        assertEquals(CONT_AUG_QNAME, data.getIdentifier().getNodeType());
+        assertEquals(expectedData, data);
+    }
 }
index fe32b1c69cb4ba8e0a5160be567797c583cc1f1e..f0c3889d714bf688872809aef0189f4462a89c6e 100644 (file)
@@ -38,10 +38,14 @@ import org.opendaylight.restconf.nb.rfc8040.jersey.providers.spi.AbstractIdentif
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 public abstract class AbstractBodyReaderTest {
+    protected static final QName CONT_AUG_QNAME = QName.create("test-ns-aug", "container-aug").intern();
+    protected static final QName LEAF_AUG_QNAME = QName.create("test-ns-aug", "leaf-aug").intern();
+
     protected final MediaType mediaType;
     protected final SchemaContextHandler schemaContextHandler;
     protected final DOMMountPointService mountPointService;
@@ -53,7 +57,7 @@ public abstract class AbstractBodyReaderTest {
         schemaContextHandler = TestUtils.newSchemaContextHandler(schemaContext);
 
         mountPointService = mock(DOMMountPointService.class);
-        final DOMMountPoint mountPoint = mock(DOMMountPoint.class);
+        final var mountPoint = mock(DOMMountPoint.class);
         doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
         doReturn(Optional.of(FixedDOMSchemaService.of(schemaContext))).when(mountPoint)
             .getService(DOMSchemaService.class);
diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/test-model-aug.yang b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/test-model-aug.yang
new file mode 100644 (file)
index 0000000..21445ca
--- /dev/null
@@ -0,0 +1,29 @@
+module test-m-aug {\r
+  namespace "test-ns-aug";\r
+  prefix test-m;\r
+\r
+  import test-m {\r
+    prefix tm;\r
+  }\r
+\r
+  augment /tm:container-root/tm:container-lvl1 {\r
+    container container-aug {\r
+      leaf leaf-aug {\r
+        type string;\r
+      }\r
+\r
+      list list-aug {\r
+        key list-aug-key;\r
+        leaf list-aug-key {\r
+          type string;\r
+        }\r
+\r
+        container list-aug-container {\r
+          leaf foo {\r
+            type string;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
diff --git a/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/test-model.yang b/restconf/restconf-nb-rfc8040/src/test/resources/instanceidentifier/yang/test-model.yang
new file mode 100644 (file)
index 0000000..39af1d1
--- /dev/null
@@ -0,0 +1,27 @@
+module test-m {\r
+  namespace "test-ns";\r
+  prefix test-m;\r
+\r
+  container container-root {\r
+    leaf leaf-lvl1 {\r
+      type string;\r
+    }\r
+\r
+    container container-lvl1 {\r
+      leaf leaf-lvl2 {\r
+        type string;\r
+      }\r
+\r
+      list list-lvl2 {\r
+        key list-lvl2-key;\r
+        leaf list-lvl2-key {\r
+          type string;\r
+        }\r
+\r
+        leaf leaf-lvl2 {\r
+          type string;\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r