fix of Bug 314 54/4454/2
authorMartin Sunal <msunal@cisco.com>
Mon, 20 Jan 2014 17:10:47 +0000 (18:10 +0100)
committerMartin Sunal <msunal@cisco.com>
Tue, 21 Jan 2014 15:18:21 +0000 (16:18 +0100)
- corrected POST operation

Change-Id: Ia5cbf9dcf662c15dc8c25f0dada359e78091ee87
Signed-off-by: Martin Sunal <msunal@cisco.com>
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/full-versions/test-data2/data3.xml [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml [new file with mode: 0644]

index 8956f37..627e2a9 100644 (file)
@@ -207,10 +207,15 @@ class JsonMapper {
                 }
                 writer.value(moduleName + ":" + valueFromDTO.getValue());
             } else {
+                Object value = node.getValue();
                 logger.debug("Value of " + baseType.getQName().getNamespace() + ":"
                         + baseType.getQName().getLocalName() + " is not instance of " + QName.class + " but is "
-                        + node.getValue().getClass());
-                writer.value(String.valueOf(node.getValue()));
+                        + (value != null ? value.getClass() : "null"));
+                if (value == null) {
+                    writer.value("");
+                } else {
+                    writer.value(String.valueOf(value));
+                }
             }
         } else if (baseType instanceof DecimalTypeDefinition || baseType instanceof IntegerTypeDefinition
                 || baseType instanceof UnsignedIntegerTypeDefinition) {
index d0099bb..b268103 100644 (file)
@@ -45,7 +45,7 @@ class ControllerContext implements SchemaServiceListener {
     val static NULL_VALUE = "null"
     val static MOUNT_MODULE = "yang-ext"
     val static MOUNT_NODE = "mount"
-    val static MOUNT = "yang-ext:mount"
+    public val static MOUNT = "yang-ext:mount"
 
     @Property
     var SchemaContext globalSchema;
index 0f53e56..b134f9b 100644 (file)
@@ -30,10 +30,12 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition
 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
 
 import static javax.ws.rs.core.Response.Status.*
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
 
 class RestconfImpl implements RestconfService {
 
     val static RestconfImpl INSTANCE = new RestconfImpl
+    val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
 
     @Property
     BrokerFacade broker
@@ -157,16 +159,30 @@ class RestconfImpl implements RestconfService {
             throw new ResponseException(BAD_REQUEST,
                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
         }
-        val uncompleteInstIdWithData = identifier.toInstanceIdentifier
-        val schemaNode = uncompleteInstIdWithData.mountPoint.findModule(payload)?.getSchemaChildNode(payload)
-        val value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
-        val completeInstIdWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
+        var InstanceIdWithSchemaNode iiWithData;
+        var CompositeNode value;
+        if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
+            if (identifier.endsWithMountPoint) {
+                throw new ResponseException(BAD_REQUEST,
+                "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
+            }
+            val completIdentifier = identifier.addMountPointIdentifier
+            iiWithData = completIdentifier.toInstanceIdentifier
+            value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
+        } else {
+            val uncompleteInstIdWithData = identifier.toInstanceIdentifier
+            val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
+            val namespace = uncompleteInstIdWithData.mountPoint.findModule(payload)?.namespace
+            val schemaNode = parentSchema.findInstanceDataChild(payload.name, namespace)
+            value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
+            iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
+        }
         var RpcResult<TransactionStatus> status = null
-        if (completeInstIdWithData.mountPoint !== null) {
-            status = broker.commitConfigurationDataPostBehindMountPoint(completeInstIdWithData.mountPoint,
-                completeInstIdWithData.instanceIdentifier, value)?.get();
+        if (iiWithData.mountPoint !== null) {
+            status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
+                iiWithData.instanceIdentifier, value)?.get();
         } else {
-            status = broker.commitConfigurationDataPost(completeInstIdWithData.instanceIdentifier, value)?.get();
+            status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
         }
         if (status === null) {
             return Response.status(ACCEPTED).build
@@ -182,7 +198,12 @@ class RestconfImpl implements RestconfService {
             throw new ResponseException(BAD_REQUEST,
                 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
         }
-        val schemaNode = findModule(null, payload)?.getSchemaChildNode(payload)
+        val module = findModule(null, payload)
+        if (module === null) {
+            throw new ResponseException(BAD_REQUEST,
+                "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
+        }
+        val schemaNode = module.findInstanceDataChild(payload.name, module.namespace)
         val value = normalizeNode(payload, schemaNode, null)
         val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
         var RpcResult<TransactionStatus> status = null
@@ -223,6 +244,14 @@ class RestconfImpl implements RestconfService {
     private def dispatch URI namespace(CompositeNodeWrapper data) {
         return data.namespace
     }
+    
+    private def dispatch String localName(CompositeNode data) {
+        return data.nodeType.localName
+    }
+    
+    private def dispatch String localName(CompositeNodeWrapper data) {
+        return data.localName
+    }
 
     private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
         if (mountPoint !== null) {
@@ -249,14 +278,14 @@ class RestconfImpl implements RestconfService {
         return module
     }
     
-    private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNode data) {
-        return parentSchemaNode?.getDataChildByName(data.nodeType.localName)
+    private def dispatch getName(CompositeNode data) {
+        return data.nodeType.localName
     }
     
-    private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNodeWrapper data) {
-        return parentSchemaNode?.getDataChildByName(data.localName)
+    private def dispatch getName(CompositeNodeWrapper data) {
+        return data.localName
     }
-
+    
     private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
         CompositeNode data, DataSchemaNode schemaOfData) {
         val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
@@ -288,7 +317,23 @@ class RestconfImpl implements RestconfService {
         }
         return keyValues
     }
-
+    
+    private def endsWithMountPoint(String identifier) {
+        return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
+    }
+    
+    private def representsMountPointRootData(CompositeNode data) {
+        return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
+            data.localName == SchemaContext.NAME.localName)
+    }
+    
+    private def addMountPointIdentifier(String identifier) {
+        if (identifier.endsWith("/")) {
+            return identifier + ControllerContext.MOUNT
+        }
+        return identifier + "/" + ControllerContext.MOUNT
+    }
+    
     private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
         if (schema === null) {
             throw new ResponseException(INTERNAL_SERVER_ERROR, "Data schema node was not found for " + node?.nodeType?.localName)
@@ -304,7 +349,7 @@ class RestconfImpl implements RestconfService {
         }
         return node
     }
-
+    
     private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
         MountInstance mountPoint) {
         if (schema === null) {
@@ -325,7 +370,7 @@ class RestconfImpl implements RestconfService {
             moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
         }
         if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
-            nodeBuilder.namespace.toString == moduleName) {
+            nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
             nodeBuilder.qname = validQName
         } else {
             throw new ResponseException(BAD_REQUEST,
index 833d030..819b5f8 100644 (file)
@@ -43,9 +43,11 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 public class RestPostOperationTest extends JerseyTest {
 
     private static String xmlDataAbsolutePath;
+    private static String xmlDataInterfaceAbsolutePath;
     private static String xmlDataRpcInput;
     private static CompositeNodeWrapper cnSnDataOutput;
     private static String xmlData2;
+    private static String xmlData3;
 
     private static ControllerContext controllerContext;
     private static BrokerFacade brokerFacade;
@@ -115,13 +117,13 @@ public class RestPostOperationTest extends JerseyTest {
         controllerContext.setSchemas(schemaContextYangsIetf);
         mockCommitConfigurationDataPostMethod(TransactionStatus.COMMITED);
         String uri = createUri("/config/", "ietf-interfaces:interfaces");
-        assertEquals(204, post(uri, MediaType.APPLICATION_XML, xmlDataAbsolutePath));
+        assertEquals(204, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
         
         mockCommitConfigurationDataPostMethod(null);
-        assertEquals(202, post(uri, MediaType.APPLICATION_XML, xmlDataAbsolutePath));
+        assertEquals(202, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
         
         mockCommitConfigurationDataPostMethod(TransactionStatus.FAILED);
-        assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataAbsolutePath));
+        assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
     }
 
     @Test
@@ -129,13 +131,13 @@ public class RestPostOperationTest extends JerseyTest {
         controllerContext.setSchemas(schemaContextYangsIetf);
         mockCommitConfigurationDataPostMethod(TransactionStatus.COMMITED);
         String uri = createUri("/datastore/", "ietf-interfaces:interfaces");
-        assertEquals(204, post(uri, MediaType.APPLICATION_XML, xmlDataAbsolutePath));
+        assertEquals(204, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
         
         mockCommitConfigurationDataPostMethod(null);
-        assertEquals(202, post(uri, MediaType.APPLICATION_XML, xmlDataAbsolutePath));
+        assertEquals(202, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
         
         mockCommitConfigurationDataPostMethod(TransactionStatus.FAILED);
-        assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataAbsolutePath));
+        assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
     }
 
     @Test
@@ -154,8 +156,10 @@ public class RestPostOperationTest extends JerseyTest {
 
         ControllerContext.getInstance().setMountService(mockMountService);
 
-        String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont/cont1");
+        String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/0/yang-ext:mount");
         assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData2));
+        uri = createUri("/config/", "ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont");
+        assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData3));
     }
     
     private void mockInvokeRpc(CompositeNode result, boolean sucessful) {
@@ -185,12 +189,16 @@ public class RestPostOperationTest extends JerseyTest {
     private static void loadData() throws IOException, URISyntaxException {
         InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces_absolute_path.xml");
         xmlDataAbsolutePath = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream));
+        xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces_interface_absolute_path.xml");
+        xmlDataInterfaceAbsolutePath = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream));
         String xmlPathRpcInput = RestconfImplTest.class.getResource("/full-versions/test-data2/data-rpc-input.xml")
                 .getPath();
         xmlDataRpcInput = TestUtils.loadTextFile(xmlPathRpcInput);
         cnSnDataOutput = prepareCnSnRpcOutput();
         String data2Input = RestconfImplTest.class.getResource("/full-versions/test-data2/data2.xml").getPath();
         xmlData2 = TestUtils.loadTextFile(data2Input);
+        String data3Input = RestconfImplTest.class.getResource("/full-versions/test-data2/data3.xml").getPath();
+        xmlData3 = TestUtils.loadTextFile(data3Input);
     }
 
     private static CompositeNodeWrapper prepareCnSnRpcOutput() throws URISyntaxException {
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/full-versions/test-data2/data3.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/full-versions/test-data2/data3.xml
new file mode 100644 (file)
index 0000000..b7b05d1
--- /dev/null
@@ -0,0 +1,4 @@
+<cont1 xmlns="test:module">
+ <lf11>lf1 data</lf11>
+ <lf12>lf2 data</lf12>
+</cont1>
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml b/opendaylight/md-sal/sal-rest-connector/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml
new file mode 100644 (file)
index 0000000..19569b5
--- /dev/null
@@ -0,0 +1,6 @@
+<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
+  <name>eth0</name>
+  <type>ethernetCsmacd</type>
+  <enabled>false</enabled>
+  <description>some interface</description>
+</interface>