Added DELETE operation 39/4139/1
authorMartin Sunal <msunal@cisco.com>
Fri, 10 Jan 2014 15:51:42 +0000 (16:51 +0100)
committerMartin Sunal <msunal@cisco.com>
Fri, 10 Jan 2014 15:54:56 +0000 (16:54 +0100)
- fix of bug 266
- corrected @Produces and @Consumes annotations in RestconfService and RestconfServiceLegacy

Change-Id: I85b49247f0ff1cfecb3ab999d6cc4f56311947e4
Signed-off-by: Martin Sunal <msunal@cisco.com>
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfServiceLegacy.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.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/NormalizeNodeTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java

index 5f9802b227ba911b4abe91dbd0832ea281004b13..60a8f285a2cc17188dcd4ea1cbb25055bb171a81 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.sal.rest.api;
 
  */
 package org.opendaylight.controller.sal.rest.api;
 
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -66,8 +68,18 @@ public interface RestconfService extends RestconfServiceLegacy {
     @Produces({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML,
                Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     @Produces({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML,
                Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+    @Consumes({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML,
+               Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
+               MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public StructuredData invokeRpc(@PathParam("identifier") String identifier, CompositeNode payload);
     
     public StructuredData invokeRpc(@PathParam("identifier") String identifier, CompositeNode payload);
     
+    @POST
+    @Path("/operations/{identifier}")
+    @Produces({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML,
+               Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
+               MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
+    public StructuredData invokeRpc(@PathParam("identifier") String identifier);
+    
     @GET
     @Path("/config/{identifier:.+}")
     @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
     @GET
     @Path("/config/{identifier:.+}")
     @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
@@ -82,20 +94,24 @@ public interface RestconfService extends RestconfServiceLegacy {
 
     @PUT
     @Path("/config/{identifier:.+}")
 
     @PUT
     @Path("/config/{identifier:.+}")
-    @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
+    @Consumes({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @POST
     @Path("/config/{identifier:.+}")
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @POST
     @Path("/config/{identifier:.+}")
-    @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
+    @Consumes({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @POST
     @Path("/config")
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @POST
     @Path("/config")
-    @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
+    @Consumes({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response createConfigurationData(CompositeNode payload);
 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response createConfigurationData(CompositeNode payload);
 
+    @DELETE
+    @Path("/config/{identifier:.+}")
+    public Response deleteConfigurationData(@PathParam("identifier") String identifier);
+
 }
 }
index 35da98b1a0db82e72d6cb32de8e0e34c858e1a41..9b69c1f09a9a3f546259b9cb4d0bc993ebb90b98 100644 (file)
@@ -1,5 +1,6 @@
 package org.opendaylight.controller.sal.rest.api;
 
 package org.opendaylight.controller.sal.rest.api;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -33,14 +34,14 @@ public interface RestconfServiceLegacy {
     @Deprecated
     @POST
     @Path("/datastore/{identifier:.+}")
     @Deprecated
     @POST
     @Path("/datastore/{identifier:.+}")
-    @Produces({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML, 
+    @Consumes({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response createConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @Deprecated
     @PUT
     @Path("/datastore/{identifier:.+}")
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response createConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @Deprecated
     @PUT
     @Path("/datastore/{identifier:.+}")
-    @Produces({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML, 
+    @Consumes({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML, 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response updateConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
 
                MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
     public Response updateConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
 
index af8f7f469d71ec833e08e16952e75178c976f5b2..343601865d80bfeaf3ca5899bdb59c08cee25041 100644 (file)
@@ -62,7 +62,7 @@ class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
         val transaction = dataService.beginTransaction;
         LOG.info("Put Configuration via Restconf: {}", path)
         transaction.putConfigurationData(path, payload);
         val transaction = dataService.beginTransaction;
         LOG.info("Put Configuration via Restconf: {}", path)
         transaction.putConfigurationData(path, payload);
-        return transaction.commit()
+        return transaction.commit
     }
 
     def commitConfigurationDataPost(InstanceIdentifier path, CompositeNode payload) {
     }
 
     def commitConfigurationDataPost(InstanceIdentifier path, CompositeNode payload) {
@@ -71,10 +71,17 @@ class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
         transaction.putConfigurationData(path, payload);
         if (payload == transaction.createdConfigurationData.get(path)) {
             LOG.info("Post Configuration via Restconf: {}", path)
         transaction.putConfigurationData(path, payload);
         if (payload == transaction.createdConfigurationData.get(path)) {
             LOG.info("Post Configuration via Restconf: {}", path)
-            return transaction.commit()
+            return transaction.commit
         }
         LOG.info("Post Configuration via Restconf was not executed because data already exists: {}", path)
         return null;
     }
 
         }
         LOG.info("Post Configuration via Restconf was not executed because data already exists: {}", path)
         return null;
     }
 
+    def commitConfigurationDataDelete(InstanceIdentifier path) {
+        checkPreconditions
+        val transaction = dataService.beginTransaction;
+        transaction.removeConfigurationData(path)
+        return transaction.commit
+    }
+
 }
 }
index 5f901c8c18f3b887b933c509e4ead4178f9df6d0..a65c0ff97aa73324083a0826186462d59c7006c6 100644 (file)
@@ -25,6 +25,7 @@ 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.type.IdentityrefTypeDefinition
 
 import static javax.ws.rs.core.Response.Status.*
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition
 
 class RestconfImpl implements RestconfService {
 
 
 class RestconfImpl implements RestconfService {
 
@@ -60,14 +61,26 @@ class RestconfImpl implements RestconfService {
     }
 
     override invokeRpc(String identifier, CompositeNode payload) {
     }
 
     override invokeRpc(String identifier, CompositeNode payload) {
-        val rpc = identifier.rpcDefinition
+        return callRpc(identifier.rpcDefinition, payload)
+    }
+    
+    override invokeRpc(String identifier) {
+        return callRpc(identifier.rpcDefinition, null)
+    }
+    
+    private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
         if (rpc === null) {
             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
         }
         if (rpc === null) {
             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
         }
-        val value = normalizeNode(payload, rpc.input, null)
-        val List<Node<?>> input = new ArrayList
-        input.add(value)
-        val rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
+        var CompositeNode rpcRequest;
+        if (payload === null) {
+            rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
+        } else {
+            val value = normalizeNode(payload, rpc.input, null)
+            val List<Node<?>> input = new ArrayList
+            input.add(value)
+            rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
+        }
         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
         if (!rpcResult.successful) {
             throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
         if (!rpcResult.successful) {
             throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
@@ -145,6 +158,15 @@ class RestconfImpl implements RestconfService {
             default: Response.status(INTERNAL_SERVER_ERROR).build
         }
     }
             default: Response.status(INTERNAL_SERVER_ERROR).build
         }
     }
+    
+    override deleteConfigurationData(String identifier) {
+        val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
+        val status = broker.commitConfigurationDataDelete(instanceIdentifierWithSchemaNode.getInstanceIdentifier).get;
+        switch status.result {
+            case TransactionStatus.COMMITED: Response.status(OK).build
+            default: Response.status(INTERNAL_SERVER_ERROR).build
+        }
+    }
 
     private def InstanceIdWithSchemaNode resolveInstanceIdentifier(String identifier) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
 
     private def InstanceIdWithSchemaNode resolveInstanceIdentifier(String identifier) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
@@ -208,7 +230,7 @@ class RestconfImpl implements RestconfService {
         for (key : listNode.keyDefinition) {
             val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
             if (dataNodeKeyValueObject === null) {
         for (key : listNode.keyDefinition) {
             val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
             if (dataNodeKeyValueObject === null) {
-                throw new ResponseException(BAD_REQUEST, "List " + dataNode.nodeType.localName + " does not contain key: " + key.localName)
+                throw new ResponseException(BAD_REQUEST, "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" + key.localName + "\"")
             }
             keyValues.put(key, dataNodeKeyValueObject);
         }
             }
             keyValues.put(key, dataNodeKeyValueObject);
         }
@@ -236,7 +258,7 @@ class RestconfImpl implements RestconfService {
         InstanceIdentifier mountPoint) {
         if (schema === null) {
             throw new ResponseException(BAD_REQUEST,
         InstanceIdentifier mountPoint) {
         if (schema === null) {
             throw new ResponseException(BAD_REQUEST,
-                "Data has bad format\n" + nodeBuilder.localName + " does not exist in yang schema.");
+                "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
         }
         var validQName = schema.QName
         var currentAugment = previousAugment;
         }
         var validQName = schema.QName
         var currentAugment = previousAugment;
@@ -254,9 +276,9 @@ class RestconfImpl implements RestconfService {
             nodeBuilder.qname = validQName
         } else {
             throw new ResponseException(BAD_REQUEST,
             nodeBuilder.qname = validQName
         } else {
             throw new ResponseException(BAD_REQUEST,
-                "Data has bad format.\nIf data is in XML format then namespace for " + nodeBuilder.localName +
-                    " should be " + schema.QName.namespace + ".\nIf data is in Json format then module name for " +
-                    nodeBuilder.localName + " should be " + moduleName + ".");
+                "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
+                    "\" should be \"" + schema.QName.namespace + "\".\nIf data is in Json format then module name for \"" +
+                    nodeBuilder.localName + "\" should be \"" + moduleName + "\".");
         }
 
         if (nodeBuilder instanceof CompositeNodeWrapper) {
         }
 
         if (nodeBuilder instanceof CompositeNodeWrapper) {
@@ -277,7 +299,7 @@ class RestconfImpl implements RestconfService {
                     }
                     if (!foundKey) {
                         throw new ResponseException(BAD_REQUEST,
                     }
                     if (!foundKey) {
                         throw new ResponseException(BAD_REQUEST,
-                            "Missing key \"" + listKey.localName + "\" of list \"" + schema.QName.localName + "\"")
+                            "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName + "\"")
                     }
                 }
             }
                     }
                 }
             }
index 1573b570287a02f8531381743c996dd6f6061962..83e6ae51d07b1e32844a712edef2d86c2a2cc39c 100644 (file)
@@ -1,8 +1,6 @@
 package org.opendaylight.controller.sal.restconf.impl.test;
 
 package org.opendaylight.controller.sal.restconf.impl.test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.*;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -23,15 +21,13 @@ public class NormalizeNodeTest extends YangAndXmlAndDataSchemaLoader {
 
     @Test
     public void namespaceNotNullAndInvalidNamespaceAndNoModuleNameTest() {
 
     @Test
     public void namespaceNotNullAndInvalidNamespaceAndNoModuleNameTest() {
-        String exceptionMessage = null;
+        boolean exceptionReised = false;
         try {
             TestUtils.normalizeCompositeNode(prepareCnSn("wrongnamespace"), modules, schemaNodePath);
         } catch (ResponseException e) {
         try {
             TestUtils.normalizeCompositeNode(prepareCnSn("wrongnamespace"), modules, schemaNodePath);
         } catch (ResponseException e) {
-            exceptionMessage = String.valueOf(e.getResponse().getEntity());
+            exceptionReised = true;
         }
         }
-        assertEquals(
-                exceptionMessage,
-                "Data has bad format.\nIf data is in XML format then namespace for cont should be normalize:node:module.\nIf data is in Json format then module name for cont should be normalize-node-module.");
+        assertTrue(exceptionReised);
     }
 
     @Test
     }
 
     @Test
index 956a27466faad5ccd5e89134ee8f1fbcd1f6c780..7cce34ffb69dc8954da2e87535585347502eeaca 100644 (file)
@@ -170,10 +170,12 @@ public class XmlProvidersTest extends JerseyTest {
         assertEquals(204, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
         assertEquals(204, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
+        entity = Entity.entity(xml, MEDIA_TYPE);
         response = target(uri).request(MEDIA_TYPE).put(entity);
         assertEquals(200, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces");
         response = target(uri).request(MEDIA_TYPE).put(entity);
         assertEquals(200, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces");
+        entity = Entity.entity(xml, MEDIA_TYPE);
         response = target(uri).request(MEDIA_TYPE).post(entity);
         assertEquals(204, response.getStatus());
     }
         response = target(uri).request(MEDIA_TYPE).post(entity);
         assertEquals(204, response.getStatus());
     }
@@ -202,9 +204,10 @@ public class XmlProvidersTest extends JerseyTest {
         assertEquals(500, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
         assertEquals(500, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
-        response = target(uri).request(MEDIA_TYPE).put(entity);
+        entity = Entity.entity(xml, MEDIA_TYPE);
+        response = target(uri).request().put(entity);
         assertEquals(500, response.getStatus());
         assertEquals(500, response.getStatus());
-        response = target(uri).request(MEDIA_TYPE).post(entity);
+        response = target(uri).request().accept(MEDIA_TYPE).post(entity);
         assertEquals(500, response.getStatus());
     }
 
         assertEquals(500, response.getStatus());
     }