Corrected response status codes from restconf 96/3496/2
authormsunal <msunal@cisco.com>
Thu, 5 Dec 2013 10:35:43 +0000 (11:35 +0100)
committermsunal <msunal@cisco.com>
Mon, 9 Dec 2013 13:20:11 +0000 (14:20 +0100)
- POST /restconf/config/* - 204 no msg body
- POST /restconf/operations/* - 200 msg body or 204 no msg body or 500 operation failed
- PUT /restconf/config/* - 200 no msg body

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

index 7022db2bc9bba631b2b21f50f2bb2a20d395d7d5..04114fa0edd3d536cb9eb408663ab3404789d3a3 100644 (file)
@@ -17,6 +17,7 @@ import javax.ws.rs.ext.Provider;
 import org.opendaylight.controller.sal.rest.api.Draft01;
 import org.opendaylight.controller.sal.rest.api.Draft02;
 import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.restconf.impl.ResponseException;
 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -45,7 +46,7 @@ public enum StructuredDataToJsonProvider implements MessageBodyWriter<Structured
             throws IOException, WebApplicationException {
         CompositeNode data = t.getData();
         if (data == null) {
-            throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
+            throw new ResponseException(Response.Status.NOT_FOUND, "No data exists.");
         }
 
         JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, "UTF-8"));
index 35352e0819c9720d4bc23e80f30437b542351633..94a7756da65cefd02adbe3a00eecb69efe9a8dab 100644 (file)
@@ -18,9 +18,10 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
 import org.opendaylight.yangtools.yang.common.QName
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
+import static javax.ws.rs.core.Response.Status.*
 
 class RestconfImpl implements RestconfService {
-    
+
     val static RestconfImpl INSTANCE = new RestconfImpl
 
     @Property
@@ -28,13 +29,13 @@ class RestconfImpl implements RestconfService {
 
     @Property
     extension ControllerContext controllerContext
-    
+
     private new() {
         if (INSTANCE !== null) {
             throw new IllegalStateException("Already instantiated");
         }
     }
-    
+
     static def getInstance() {
         return INSTANCE
     }
@@ -61,141 +62,148 @@ class RestconfImpl implements RestconfService {
     override createConfigurationData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
         val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode)
-        val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get();
         switch status.result {
-            case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
-            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+            case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
+            default: Response.status(INTERNAL_SERVER_ERROR).build
         }
     }
 
     override updateConfigurationData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.resolveInstanceIdentifier
         val value = normalizeNode(payload, identifierWithSchemaNode.schemaNode)
-        val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier, value).get();
         switch status.result {
-            case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
-            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+            case TransactionStatus.COMMITED: Response.status(OK).build
+            default: Response.status(INTERNAL_SERVER_ERROR).build
         }
     }
 
     override invokeRpc(String identifier, CompositeNode payload) {
         val rpc = identifier.rpcDefinition
         if (rpc === null) {
-            throw new ResponseException(Response.Status.NOT_FOUND, "RPC does not exist.");
+            throw new ResponseException(NOT_FOUND, "RPC does not exist.");
         }
         val value = normalizeNode(payload, rpc.input)
         val List<Node<?>> input = new ArrayList
         input.add(value)
         val rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
-        return new StructuredData(rpcResult.result, rpc.output);
+        if (!rpcResult.successful) {
+            throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
+        }
+        if (rpcResult.result === null) {
+            return null
+        }
+        return new StructuredData(rpcResult.result, rpc.output)
     }
-    
+
     override readConfigurationData(String identifier) {
         val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
         val data = broker.readConfigurationData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
         return new StructuredData(data, instanceIdentifierWithSchemaNode.schemaNode)
     }
-    
+
     override readOperationalData(String identifier) {
         val instanceIdentifierWithSchemaNode = identifier.resolveInstanceIdentifier
         val data = broker.readOperationalData(instanceIdentifierWithSchemaNode.getInstanceIdentifier);
         return new StructuredData(data, instanceIdentifierWithSchemaNode.schemaNode)
     }
-    
+
     override updateConfigurationDataLegacy(String identifier, CompositeNode payload) {
-        updateConfigurationData(identifier,payload);
+        updateConfigurationData(identifier, payload);
     }
-    
+
     override createConfigurationDataLegacy(String identifier, CompositeNode payload) {
-        createConfigurationData(identifier,payload);
+        createConfigurationData(identifier, payload);
     }
-    
+
     private def InstanceIdWithSchemaNode resolveInstanceIdentifier(String identifier) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
         if (identifierWithSchemaNode === null) {
-            throw new ResponseException(Response.Status.BAD_REQUEST, "URI has bad format");
+            throw new ResponseException(BAD_REQUEST, "URI has bad format");
         }
         return identifierWithSchemaNode
     }
-    
+
     private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema) {
         if (node instanceof CompositeNodeWrapper) {
-            normalizeNode(node as CompositeNodeWrapper, schema,null)
+            normalizeNode(node as CompositeNodeWrapper, schema, null)
             return (node as CompositeNodeWrapper).unwrap()
         }
         return node
     }
 
-    private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema,QName previousAugment) {
+    private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment) {
         if (schema === null) {
-            throw new ResponseException(Response.Status.BAD_REQUEST,
+            throw new ResponseException(BAD_REQUEST,
                 "Data has bad format\n" + nodeBuilder.localName + " does not exist in yang schema.");
         }
         var validQName = schema.QName
         var currentAugment = previousAugment;
-        if(schema.augmenting) {
+        if (schema.augmenting) {
             currentAugment = schema.QName
-        } else if(previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
-            validQName = QName.create(currentAugment,schema.QName.localName);
+        } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
+            validQName = QName.create(currentAugment, schema.QName.localName);
         }
         val moduleName = controllerContext.findModuleByNamespace(validQName.namespace);
         if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
             nodeBuilder.namespace.path == moduleName) {
             nodeBuilder.qname = validQName
         } else {
-            throw new ResponseException(Response.Status.BAD_REQUEST,
+            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 + ".\n If data is in Json format then module name for " +
                     nodeBuilder.localName + " should be " + moduleName + ".");
         }
-        
+
         if (nodeBuilder instanceof CompositeNodeWrapper) {
             val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
             for (child : children) {
                 normalizeNode(child,
-                    findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),currentAugment)
+                    findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),
+                    currentAugment)
             }
         } else if (nodeBuilder instanceof SimpleNodeWrapper) {
             val simpleNode = (nodeBuilder as SimpleNodeWrapper)
             val stringValue = simpleNode.value as String;
-            
+
             val objectValue = TypeDefinitionAwareCodec.from(schema.typeDefinition)?.deserialize(stringValue);
             simpleNode.setValue(objectValue)
         } else if (nodeBuilder instanceof EmptyNodeWrapper) {
             val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
-            if(schema instanceof LeafSchemaNode) {
+            if (schema instanceof LeafSchemaNode) {
                 emptyNodeBuilder.setComposite(false);
-            } else if(schema instanceof ContainerSchemaNode) {
+            } else if (schema instanceof ContainerSchemaNode) {
+
                 // FIXME: Add presence check
                 emptyNodeBuilder.setComposite(true);
             }
         }
     }
-    
+
     private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
         node.type
     }
-    
+
     private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
         node.type
     }
-    
-    
+
     private def DataSchemaNode findFirstSchemaByLocalName(String localName, Set<DataSchemaNode> schemas) {
         for (schema : schemas) {
             if (schema instanceof ChoiceNode) {
                 for (caze : (schema as ChoiceNode).cases) {
-                    val result =  findFirstSchemaByLocalName(localName, caze.childNodes)
+                    val result = findFirstSchemaByLocalName(localName, caze.childNodes)
                     if (result !== null) {
                         return result
                     }
                 }
             } else {
                 val result = schemas.findFirst[n|n.QName.localName.equals(localName)]
-                if(result !== null) {
+                if (result !== null) {
                     return result;
-                
+
                 }
             }
         }
index 103c9ed3cdbe04f3f192180da77d6fd8fc14945e..d58b7e9dab0cecac48f4d5641dd2b6ff9bc8ba3a 100644 (file)
@@ -27,7 +27,7 @@ public class InvokeRpcMethodTest {
         @Override
         public RpcResult<CompositeNode> answer(InvocationOnMock invocation) throws Throwable {
             CompositeNode compNode = (CompositeNode) invocation.getArguments()[1];
-            return new DummyRpcResult.Builder<CompositeNode>().result(compNode).build();
+            return new DummyRpcResult.Builder<CompositeNode>().result(compNode).isSuccessful(true).build();
         }
     }
 
index 5b0eea32126e5f85dc5c95d2c7b0ef52624c9a9d..ec7dba67b9a35392845e93588862411f45362b49 100644 (file)
@@ -133,21 +133,21 @@ public class XmlProvidersTest extends JerseyTest {
 
         String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
         Response response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity);
-        assertEquals(204, response.getStatus());
-        response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
         assertEquals(200, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
+        assertEquals(204, response.getStatus());
 
         uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
         response = target(uri).request(MEDIA_TYPE_DRAFT02).put(entity);
-        assertEquals(204, response.getStatus());
-        response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
         assertEquals(200, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE_DRAFT02).post(entity);
+        assertEquals(204, response.getStatus());
 
         uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
         response = target(uri).request(MEDIA_TYPE).put(entity);
-        assertEquals(204, response.getStatus());
-        response = target(uri).request(MEDIA_TYPE).post(entity);
         assertEquals(200, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(204, response.getStatus());
     }
 
     @Test