Support leaf-nodes-only in RFC8040 northbound 32/98132/2
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 26 Oct 2021 08:43:37 +0000 (10:43 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 26 Oct 2021 11:04:05 +0000 (13:04 +0200)
We have somehow lost (or never had) parity with bierman02 extension
for this option. The code to support it is in place, but due to the
legacy structure it fell off the truck in terms of parsing URIs, so
it could never be activated for a request.

Add the proper structure to support it again and define a capability
URI to let users know it is available.

JIRA: NETCONF-824
Change-Id: Ib05cc361d6bc3cc534f598276ecc84c9353f4cc2
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/LeafNodesOnlyParam.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/NotificationQueryParams.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/databind/jaxrs/QueryParams.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextHandler.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/SubscribeToStreamUtil.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractQueryParams.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextHandlerTest.java
restconf/restconf-nb-rfc8040/src/test/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapterTest.java

diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/LeafNodesOnlyParam.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/LeafNodesOnlyParam.java
new file mode 100644 (file)
index 0000000..266bc24
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.nb.rfc8040;
+
+import java.net.URI;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * OpenDaylight extension parameter. When used as {@code odl-leaf-nodes-only=true}, it will instruct the listener
+ * streams to only emit leaf nodes.
+ */
+public final class LeafNodesOnlyParam implements RestconfQueryParam<LeafNodesOnlyParam> {
+    // API consistency: must not be confused with enum constants
+    @SuppressWarnings("checkstyle:ConstantName")
+    public static final String uriName = "odl-leaf-nodes-only";
+
+    private static final @NonNull URI CAPABILITY =
+        URI.create("urn:opendaylight:params:restconf:capability:leaf-nodes-only:1.0");
+    private static final @NonNull LeafNodesOnlyParam FALSE = new LeafNodesOnlyParam(false);
+    private static final @NonNull LeafNodesOnlyParam TRUE = new LeafNodesOnlyParam(true);
+
+    private final boolean value;
+
+    private LeafNodesOnlyParam(final boolean value) {
+        this.value = value;
+    }
+
+    public static @NonNull LeafNodesOnlyParam of(final boolean value) {
+        return value ? TRUE : FALSE;
+    }
+
+    public static @NonNull LeafNodesOnlyParam forUriValue(final String uriValue) {
+        switch (uriValue) {
+            case "false":
+                return FALSE;
+            case "true":
+                return TRUE;
+            default:
+                throw new IllegalArgumentException("Value can be 'false' or 'true', not '" + uriValue + "'");
+        }
+    }
+
+    @Override
+    public Class<@NonNull LeafNodesOnlyParam> javaClass() {
+        return LeafNodesOnlyParam.class;
+    }
+
+    @Override
+    public String paramName() {
+        return uriName;
+    }
+
+    @Override
+    public String paramValue() {
+        return String.valueOf(value);
+    }
+
+    public boolean value() {
+        return value;
+    }
+
+    public static @NonNull URI capabilityUri() {
+        return CAPABILITY;
+    }
+}
index 1894198761ef3731d7f60888429c63df88c3422f..680057d2b53656f0e7d606a3e236df9abc9db6dc 100644 (file)
@@ -20,23 +20,27 @@ import org.opendaylight.yangtools.concepts.Immutable;
  */
 public final class NotificationQueryParams implements Immutable {
     private final SkipNotificationDataParam skipNotificationData;
+    private final LeafNodesOnlyParam leafNodesOnly;
     private final StartTimeParam startTime;
     private final StopTimeParam stopTime;
     private final FilterParam filter;
 
     private NotificationQueryParams(final StartTimeParam startTime, final StopTimeParam stopTime,
-            final FilterParam filter, final SkipNotificationDataParam skipNotificationData) {
+            final FilterParam filter, final LeafNodesOnlyParam leafNodesOnly,
+            final SkipNotificationDataParam skipNotificationData) {
         this.startTime = startTime;
         this.stopTime = stopTime;
         this.filter = filter;
+        this.leafNodesOnly = leafNodesOnly;
         this.skipNotificationData = skipNotificationData;
     }
 
     public static @NonNull NotificationQueryParams of(final StartTimeParam startTime, final StopTimeParam stopTime,
-            final FilterParam filter, final SkipNotificationDataParam skipNotificationData) {
+            final FilterParam filter, final LeafNodesOnlyParam leafNodesOnly,
+            final SkipNotificationDataParam skipNotificationData) {
         checkArgument(stopTime == null || startTime != null,
             "Stop-time parameter has to be used with start-time parameter.");
-        return new NotificationQueryParams(startTime, stopTime, filter, skipNotificationData);
+        return new NotificationQueryParams(startTime, stopTime, filter, leafNodesOnly, skipNotificationData);
     }
 
     /**
@@ -66,6 +70,15 @@ public final class NotificationQueryParams implements Immutable {
         return filter;
     }
 
+    /**
+     * Get odl-leaf-nodes-only query parameter.
+     *
+     * @return odl-leaf-nodes-only
+     */
+    public @Nullable LeafNodesOnlyParam leafNodesOnly() {
+        return leafNodesOnly;
+    }
+
     /**
      * Get odl-skip-notification-data query parameter.
      *
@@ -87,6 +100,9 @@ public final class NotificationQueryParams implements Immutable {
         if (filter != null) {
             helper.add("filter", filter.paramValue());
         }
+        if (leafNodesOnly != null) {
+            helper.add("leafNodesOnly", leafNodesOnly.value());
+        }
         if (skipNotificationData != null) {
             helper.add("skipNotificationData", skipNotificationData.value());
         }
index 7788d6957986b3ec4a91b23cc8895f61d05cfbde..4848b11e97de8cdcd3db5d60141cac0d18bfac0c 100644 (file)
@@ -29,6 +29,7 @@ import org.opendaylight.restconf.nb.rfc8040.DepthParam;
 import org.opendaylight.restconf.nb.rfc8040.FieldsParam;
 import org.opendaylight.restconf.nb.rfc8040.FilterParam;
 import org.opendaylight.restconf.nb.rfc8040.InsertParam;
+import org.opendaylight.restconf.nb.rfc8040.LeafNodesOnlyParam;
 import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
 import org.opendaylight.restconf.nb.rfc8040.PointParam;
 import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
@@ -68,6 +69,7 @@ public final class QueryParams {
         StartTimeParam startTime = null;
         StopTimeParam stopTime = null;
         FilterParam filter = null;
+        LeafNodesOnlyParam leafNodesOnly = null;
         SkipNotificationDataParam skipNotificationData = null;
 
         for (Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
@@ -85,6 +87,9 @@ public final class QueryParams {
                     case StopTimeParam.uriName:
                         stopTime = optionalParam(StopTimeParam::forUriValue, paramName, paramValues);
                         break;
+                    case LeafNodesOnlyParam.uriName:
+                        leafNodesOnly = optionalParam(LeafNodesOnlyParam::forUriValue, paramName, paramValues);
+                        break;
                     case SkipNotificationDataParam.uriName:
                         skipNotificationData = optionalParam(SkipNotificationDataParam::forUriValue, paramName,
                             paramValues);
@@ -99,7 +104,7 @@ public final class QueryParams {
         }
 
         try {
-            return NotificationQueryParams.of(startTime, stopTime, filter, skipNotificationData);
+            return NotificationQueryParams.of(startTime, stopTime, filter, leafNodesOnly, skipNotificationData);
         } catch (IllegalArgumentException e) {
             throw new RestconfDocumentedException("Invalid query parameters: " + e.getMessage(), e);
         }
index f36bf96aec1a45070ce615b0d1afe2a4b37eeb36..495eb9e3401531de9012ac26c63b31f371197b63 100644 (file)
@@ -32,6 +32,7 @@ import org.opendaylight.restconf.nb.rfc8040.AbstractReplayParam;
 import org.opendaylight.restconf.nb.rfc8040.DepthParam;
 import org.opendaylight.restconf.nb.rfc8040.FieldsParam;
 import org.opendaylight.restconf.nb.rfc8040.FilterParam;
+import org.opendaylight.restconf.nb.rfc8040.LeafNodesOnlyParam;
 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
 import org.opendaylight.restconf.nb.rfc8040.SkipNotificationDataParam;
 import org.opendaylight.restconf.nb.rfc8040.WithDefaultsParam;
@@ -184,6 +185,7 @@ public class SchemaContextHandler implements EffectiveModelContextListener, Auto
                     .withChildValue(FilterParam.capabilityUri().toString())
                     .withChildValue(AbstractReplayParam.capabilityUri().toString())
                     .withChildValue(WithDefaultsParam.capabilityUri().toString())
+                    .withChildValue(LeafNodesOnlyParam.capabilityUri().toString())
                     .withChildValue(SkipNotificationDataParam.capabilityUri().toString())
                     .build())
                 .build())
index 3a6afed996f9eac5bcdb56fb738432c491d695ab..b95059beb5e3efb4198361ffab02c8df0050fe72 100644 (file)
@@ -135,7 +135,8 @@ abstract class SubscribeToStreamUtil {
                 notificationQueryParams.startTime(),
                 notificationQueryParams.stopTime(),
                 notificationQueryParams.filter(),
-                false, notificationQueryParams.skipNotificationData());
+                notificationQueryParams.leafNodesOnly(),
+                notificationQueryParams.skipNotificationData());
         final DOMDataBroker dataBroker = handlersHolder.getDataBroker();
         notificationListenerAdapter.setCloseVars(dataBroker, handlersHolder.getSchemaHandler());
         final MapEntryNode mapToStreams = RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
@@ -187,7 +188,8 @@ abstract class SubscribeToStreamUtil {
                 notificationQueryParams.startTime(),
                 notificationQueryParams.stopTime(),
                 notificationQueryParams.filter(),
-                false, notificationQueryParams.skipNotificationData());
+                notificationQueryParams.leafNodesOnly(),
+                notificationQueryParams.skipNotificationData());
 
         final DOMDataBroker dataBroker = handlersHolder.getDataBroker();
         final SchemaContextHandler schemaHandler = handlersHolder.getSchemaHandler();
index 2d9f2c11816b2ffc136bcf96536d27e39cf8e1e1..25265cf80c69e3f3dcd49d62818f91bbea8e17d4 100644 (file)
@@ -18,6 +18,7 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.nb.rfc8040.FilterParam;
+import org.opendaylight.restconf.nb.rfc8040.LeafNodesOnlyParam;
 import org.opendaylight.restconf.nb.rfc8040.SkipNotificationDataParam;
 import org.opendaylight.restconf.nb.rfc8040.StartTimeParam;
 import org.opendaylight.restconf.nb.rfc8040.StopTimeParam;
@@ -57,11 +58,11 @@ abstract class AbstractQueryParams extends AbstractNotificationsData {
      */
     @SuppressWarnings("checkstyle:hiddenField")
     public final void setQueryParams(final StartTimeParam startTime, final StopTimeParam stopTime,
-            final FilterParam filter, final boolean leafNodesOnly,
+            final FilterParam filter, final LeafNodesOnlyParam leafNodesOnly,
             final SkipNotificationDataParam skipNotificationData) {
         start = startTime == null ? Instant.now() : parseDateAndTime(startTime.value());
         stop = stopTime == null ? null : parseDateAndTime(stopTime.value());
-        this.leafNodesOnly = leafNodesOnly;
+        this.leafNodesOnly = leafNodesOnly == null ? false : leafNodesOnly.value();
         this.skipNotificationData = skipNotificationData == null ? false : skipNotificationData.value();
 
         if (filter != null) {
index e04a3a827527853e7eaa2c0f4f9c559c10a96ac3..68b40980e60b9bb08b4cd7d89ef37aabf2f10b42 100644 (file)
@@ -154,6 +154,7 @@ public class SchemaContextHandlerTest {
                 equalTo("urn:ietf:params:restconf:capability:filter:1.0"),
                 equalTo("urn:ietf:params:restconf:capability:replay:1.0"),
                 equalTo("urn:ietf:params:restconf:capability:with-defaults:1.0"),
+                equalTo("urn:opendaylight:params:restconf:capability:leaf-nodes-only:1.0"),
                 equalTo("urn:opendaylight:params:restconf:capability:skip-notification-data:1.0")));
     }
 
index 13fa69cef1691f2fa8badf33a82fe1c6e89675bf..7083a1a9a72102ebf6c5eabea5940c0d2775e61b 100644 (file)
@@ -34,6 +34,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
+import org.opendaylight.restconf.nb.rfc8040.LeafNodesOnlyParam;
 import org.opendaylight.restconf.nb.rfc8040.SkipNotificationDataParam;
 import org.opendaylight.restconf.nb.rfc8040.StartTimeParam;
 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
@@ -121,8 +122,8 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
                               final NotificationOutputTypeGrouping.NotificationOutputType outputType,
                               final boolean leafNodesOnly, final boolean skipNotificationData) {
             super(path, streamName, outputType);
-            setQueryParams(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null, leafNodesOnly,
-                SkipNotificationDataParam.of(skipNotificationData));
+            setQueryParams(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null,
+                LeafNodesOnlyParam.of(leafNodesOnly), SkipNotificationDataParam.of(skipNotificationData));
         }
 
         @Override