From 9b5e23126a416565449279462df8dcede442fefd Mon Sep 17 00:00:00 2001 From: Martin Sunal Date: Mon, 20 Jan 2014 18:10:47 +0100 Subject: [PATCH] fix of Bug 314 - corrected POST operation Change-Id: Ia5cbf9dcf662c15dc8c25f0dada359e78091ee87 Signed-off-by: Martin Sunal --- .../controller/sal/rest/impl/JsonMapper.java | 9 ++- .../sal/restconf/impl/ControllerContext.xtend | 2 +- .../sal/restconf/impl/RestconfImpl.xtend | 79 +++++++++++++++---- .../impl/test/RestPostOperationTest.java | 22 ++++-- .../full-versions/test-data2/data3.xml | 4 + ...ces_interfaces_interface_absolute_path.xml | 6 ++ 6 files changed, 95 insertions(+), 27 deletions(-) create mode 100644 opendaylight/md-sal/sal-rest-connector/src/test/resources/full-versions/test-data2/data3.xml create mode 100644 opendaylight/md-sal/sal-rest-connector/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java index 8956f37ce5..627e2a9ce1 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonMapper.java @@ -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) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend index d0099bb398..b26810347c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend @@ -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; diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend index 0f53e56b84..b134f9bf6b 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend @@ -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 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 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, diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java index 833d030b92..819b5f8213 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java @@ -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 index 0000000000..b7b05d1ea2 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/full-versions/test-data2/data3.xml @@ -0,0 +1,4 @@ + + lf1 data + lf2 data + 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 index 0000000000..19569b5598 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/parts/ietf-interfaces_interfaces_interface_absolute_path.xml @@ -0,0 +1,6 @@ + + eth0 + ethernetCsmacd + false + some interface + -- 2.36.6