Codify operationsGET 00/109000/2
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 16 Nov 2023 22:23:18 +0000 (23:23 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 18 Nov 2023 08:00:06 +0000 (09:00 +0100)
GET on /operations is well-defined, express it in RestconfServer,
migrating callers of OperationsContent.bodyFor().

JIRA: NETCONF-773
Change-Id: I64b713fdddac2dc3b1e4166a26ea554d23e3cfa1
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/MdsalRestconfServer.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/OperationsContent.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfOperationsServiceImpl.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/RestconfServer.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/Netconf822Test.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImplTest.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfOperationsServiceImplTest.java

index 55f9bdb433ae0f4158302082d066db4b5f30347a..1514a3eaf7d9cb0e3f05e0f740df0c2f909edd9b 100644 (file)
@@ -50,6 +50,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
+import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
@@ -127,6 +128,21 @@ public final class MdsalRestconfServer implements RestconfServer {
             ImmutableNodes.leafNode(YANG_LIBRARY_VERSION, YANG_LIBRARY_REVISION));
     }
 
+    @Override
+    public String operationsGET(final OperationsContent contentType) {
+        return operationsGET(contentType, bindRequestRoot().inference());
+    }
+
+    @Override
+    public String operationsGET(final OperationsContent contentType, final String operation) {
+        return operationsGET(contentType, bindRequestPath(operation).inference());
+    }
+
+    @VisibleForTesting
+    static @NonNull String operationsGET(final OperationsContent contentType, final @NonNull Inference inference) {
+        return contentType.bodyFor(inference);
+    }
+
     @Override
     public RestconfFuture<OperationOutput> operationsPOST(final URI restconfURI, final String apiPath,
             final OperationInputBody body) {
index 51e9c4ff1e442998b5162e436c44c5ae88ec6d37..ab6c075bf88e9d0012702689f21a0ee607090bf6 100644 (file)
@@ -25,7 +25,7 @@ import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference
  * RESTCONF {@code /operations} content for a {@code GET} operation as per
  * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.3.2">RFC8040</a>.
  */
-enum OperationsContent {
+public enum OperationsContent {
     JSON("{ \"ietf-restconf:operations\" : { } }") {
         @Override
         String createBody(final List<Entry<String, List<String>>> rpcsByPrefix) {
index 9def8bd0d9b886eb96c2e7773ccb68401d30d5e9..bce5f4fc909051f3e13a431809c26e61b8df9fbb 100644 (file)
@@ -57,7 +57,7 @@ public final class RestconfOperationsServiceImpl {
     @Path("/operations")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
     public String getOperationsJSON() {
-        return OperationsContent.JSON.bodyFor(server.bindRequestRoot().inference());
+        return server.operationsGET(OperationsContent.JSON);
     }
 
     /**
@@ -70,7 +70,7 @@ public final class RestconfOperationsServiceImpl {
     @Path("/operations/{identifier:.+}")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
     public String getOperationJSON(@PathParam("identifier") final String identifier) {
-        return OperationsContent.JSON.bodyFor(server.bindRequestPath(identifier).inference());
+        return server.operationsGET(OperationsContent.JSON, identifier);
     }
 
     /**
@@ -81,8 +81,8 @@ public final class RestconfOperationsServiceImpl {
     @GET
     @Path("/operations")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    public String getOperationsXML() {
-        return OperationsContent.XML.bodyFor(server.bindRequestRoot().inference());
+    public String operationsGetXML() {
+        return server.operationsGET(OperationsContent.XML);
     }
 
     /**
@@ -94,8 +94,8 @@ public final class RestconfOperationsServiceImpl {
     @GET
     @Path("/operations/{identifier:.+}")
     @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    public String getOperationXML(@PathParam("identifier") final String identifier) {
-        return OperationsContent.XML.bodyFor(server.bindRequestPath(identifier).inference());
+    public String operationsGetXML(@PathParam("identifier") final String identifier) {
+        return server.operationsGET(OperationsContent.XML, identifier);
     }
 
     /**
@@ -121,10 +121,10 @@ public final class RestconfOperationsServiceImpl {
         MediaType.APPLICATION_XML,
         MediaType.TEXT_XML
     })
-    public void invokeRpcXML(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
+    public void operationsPostXML(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
             @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
         try (var xmlBody = new XmlOperationInputBody(body)) {
-            invokeRpc(identifier, uriInfo, ar, xmlBody);
+            operationsPOST(identifier, uriInfo, ar, xmlBody);
         }
     }
 
@@ -150,14 +150,14 @@ public final class RestconfOperationsServiceImpl {
         MediaType.APPLICATION_XML,
         MediaType.TEXT_XML
     })
-    public void invokeRpcJSON(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
+    public void operationsPostJSON(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
             @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
         try (var jsonBody = new JsonOperationInputBody(body)) {
-            invokeRpc(identifier, uriInfo, ar, jsonBody);
+            operationsPOST(identifier, uriInfo, ar, jsonBody);
         }
     }
 
-    private void invokeRpc(final String identifier, final UriInfo uriInfo, final AsyncResponse ar,
+    private void operationsPOST(final String identifier, final UriInfo uriInfo, final AsyncResponse ar,
             final OperationInputBody body) {
         server.operationsPOST(uriInfo.getBaseUri(), identifier, body)
             .addCallback(new JaxRsRestconfCallback<OperationOutput>(ar) {
index 431bc262b0bf365dc8e2d6de7700cb0931678a99..435959c55c85d577d2268bbb5df7bfa79bbb6041 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.restconf.api.ApiPath;
 import org.opendaylight.restconf.common.errors.RestconfFuture;
 import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.OperationsContent;
 import org.opendaylight.restconf.server.spi.OperationOutput;
 
 /**
@@ -32,6 +33,28 @@ public interface RestconfServer {
     // FIXME: RestconfFuture if we transition to being used by restconf-client implementation
     NormalizedNodePayload yangLibraryVersionGET();
 
+    /**
+     * Return the set of supported RPCs supported by {@link #operationsPOST(URI, String, OperationInputBody)}.
+     *
+     * @param contentType Formatting type
+     * @return A formatted string
+     */
+    String operationsGET(OperationsContent contentType);
+
+    /*
+     * Return the details about a particular operation supported by
+     * {@link #operationsPOST(URI, String, OperationInputBody)}, as expressed in the
+     * <a href="https://www.rfc-editor.org/rfc/rfc8040#page-84>RFC8040<a> {@code container operations} statement.
+     *
+     * @param contentType Formatting type
+     * @param operation An operation
+     * @return A formatted string
+     */
+    // FIXME: 'operation' should really be an ApiIdentifier with non-null module, but we also support ang-ext:mount,
+    //        and hence it is a path right now
+    // FIXME: use ApiPath instead of String
+    String operationsGET(OperationsContent contentType, String operation);
+
     /**
      * Invoke an RPC operation, as defined in
      * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.6">RFC8040 Operation Resource</a>.
index 7c8c9bbf4506f30ec98704d5d511cd0dbd2ab767..a60cb9d448886ac1582376a850645413e413d323 100644 (file)
@@ -35,7 +35,8 @@ public class Netconf822Test {
                 "foo:new": [null],
                 "foo:new1": [null]
               }
-            }""", OperationsContent.JSON.bodyFor(SchemaInferenceStack.of(SCHEMA).toInference()));
+            }""",
+            MdsalRestconfServer.operationsGET(OperationsContent.JSON, SchemaInferenceStack.of(SCHEMA).toInference()));
     }
 
     @Test
@@ -45,7 +46,8 @@ public class Netconf822Test {
               "ietf-restconf:operations" : {
                 "foo:new1": [null]
               }
-            }""", OperationsContent.JSON.bodyFor(SchemaInferenceStack.of(SCHEMA, NEW1).toInference()));
+            }""",
+            MdsalRestconfServer.operationsGET(OperationsContent.JSON, SchemaInferenceStack.of(SCHEMA, NEW1).toInference()));
     }
 
     @Test
@@ -56,7 +58,8 @@ public class Netconf822Test {
                         xmlns:ns0="foo" >
               <ns0:new/>
               <ns0:new1/>
-            </operations>""", OperationsContent.XML.bodyFor(SchemaInferenceStack.of(SCHEMA).toInference()));
+            </operations>""",
+            MdsalRestconfServer.operationsGET(OperationsContent.XML, SchemaInferenceStack.of(SCHEMA).toInference()));
     }
 
     @Test
@@ -66,6 +69,8 @@ public class Netconf822Test {
             <operations xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"
                         xmlns:ns0="foo" >
               <ns0:new1/>
-            </operations>""", OperationsContent.XML.bodyFor(SchemaInferenceStack.of(SCHEMA, NEW1).toInference()));
+            </operations>""",
+            MdsalRestconfServer.operationsGET(OperationsContent.XML,
+                SchemaInferenceStack.of(SCHEMA, NEW1).toInference()));
     }
 }
index 789b192249b5b05d27a07fb0498c66cfa6b3d98e..8206b39bfd13be406fc01cb91a019dfd04ba4a20 100644 (file)
@@ -102,7 +102,7 @@ public class RestconfInvokeOperationsServiceImplTest {
         prepNNC(result);
         final var ar = mock(AsyncResponse.class);
         final var captor = ArgumentCaptor.forClass(Response.class);
-        invokeOperationsService.invokeRpcXML("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
+        invokeOperationsService.operationsPostXML("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
             <input xmlns="invoke:rpc:module"/>
             """.getBytes(StandardCharsets.UTF_8)), mock(UriInfo.class), ar);
         verify(ar).resume(captor.capture());
@@ -121,7 +121,7 @@ public class RestconfInvokeOperationsServiceImplTest {
         prepNNC(result);
         final var ar = mock(AsyncResponse.class);
         final var response = ArgumentCaptor.forClass(Response.class);
-        invokeOperationsService.invokeRpcJSON("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
+        invokeOperationsService.operationsPostJSON("invoke-rpc-module:rpc-test", new ByteArrayInputStream("""
             {
               "invoke-rpc-module:input" : {
               }
index 29b2e36ad954d93852fa1c89861423031df6d152..e50b171744573509c748b13088ee7a68d580d74a 100644 (file)
@@ -96,7 +96,7 @@ public class RestconfOperationsServiceImplTest {
 
     @Test
     public void testOperationsXml() {
-        final var operationsXML = opService.getOperationsXML();
+        final var operationsXML = opService.operationsGetXML();
         assertEquals(EXPECTED_XML, operationsXML);
     }
 
@@ -108,7 +108,7 @@ public class RestconfOperationsServiceImplTest {
 
     @Test
     public void testMountPointOperationsXml() {
-        final var operationXML = opService.getOperationXML(DEVICE_ID);
+        final var operationXML = opService.operationsGetXML(DEVICE_ID);
         assertEquals(EXPECTED_XML, operationXML);
     }
 
@@ -125,7 +125,7 @@ public class RestconfOperationsServiceImplTest {
 
     @Test
     public void testMountPointSpecificOperationsXml() {
-        final var operationXML = opService.getOperationXML(DEVICE_RPC1_MODULE1_ID);
+        final var operationXML = opService.operationsGetXML(DEVICE_RPC1_MODULE1_ID);
         assertEquals("""
             <?xml version="1.0" encoding="UTF-8"?>
             <operations xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf"