Clean up data{DELETE,PATCH}() 48/109148/2
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 2 Dec 2023 10:04:53 +0000 (11:04 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 2 Dec 2023 10:27:07 +0000 (11:27 +0100)
While we know what we are going to do with the ApiPath, split the lookup
and interpretation.

JIRA: NETCONF-1157
Change-Id: Ife099f18e3600194f7c922a9455d4a493ee9a28f
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/RestconfStrategy.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/mdsal/MdsalRestconfServer.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/AbstractRestconfStrategyTest.java

index 5627de980bbd56860737d38d5896e9bfefbb3a62..a07377ee7ffefca1dfc5dae8840efdd4dfe9dbd5 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.restconf.nb.rfc8040.rests.transactions;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
@@ -66,12 +67,14 @@ import org.opendaylight.restconf.nb.rfc8040.Insert;
 import org.opendaylight.restconf.nb.rfc8040.databind.ChildBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.DataPostBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
+import org.opendaylight.restconf.nb.rfc8040.databind.PatchBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.ResourceBody;
 import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
 import org.opendaylight.restconf.nb.rfc8040.utils.parser.YangInstanceIdentifierSerializer;
 import org.opendaylight.restconf.server.api.DataGetParams;
+import org.opendaylight.restconf.server.api.DataPatchPath;
 import org.opendaylight.restconf.server.api.DataPostPath;
 import org.opendaylight.restconf.server.api.DataPostResult;
 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
@@ -294,32 +297,8 @@ public abstract class RestconfStrategy {
     // FIXME: this method should only be needed in MdsalRestconfStrategy
     abstract ListenableFuture<Boolean> exists(YangInstanceIdentifier path);
 
-    /**
-     * Delete data from the configuration datastore. If the data does not exist, this operation will fail, as outlined
-     * in <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.7">RFC8040 section 4.7</a>
-     *
-     * @param path Path to delete
-     * @return A {@link RestconfFuture}
-     * @throws NullPointerException if {@code path} is {@code null}
-     */
-    public final @NonNull RestconfFuture<Empty> delete(final YangInstanceIdentifier path) {
-        final var ret = new SettableRestconfFuture<Empty>();
-        delete(ret, requireNonNull(path));
-        return ret;
-    }
-
-    abstract void delete(@NonNull SettableRestconfFuture<Empty> future, @NonNull YangInstanceIdentifier path);
-
-    /**
-     * Merge data into the configuration datastore, as outlined in
-     * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040 section 4.6.1</a>.
-     *
-     * @param path Path to merge
-     * @param data Data to merge
-     * @return A {@link RestconfFuture}
-     * @throws NullPointerException if any argument is {@code null}
-     */
-    public final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
+    @VisibleForTesting
+    final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
         final var ret = new SettableRestconfFuture<Empty>();
         merge(ret, requireNonNull(path), requireNonNull(data));
         return ret;
@@ -577,6 +556,52 @@ public abstract class RestconfStrategy {
         };
     }
 
+    /**
+     * Merge data into the configuration datastore, as outlined in
+     * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040 section 4.6.1</a>.
+     *
+     * @param apiPath Path to merge
+     * @param body Data to merge
+     * @return A {@link RestconfFuture}
+     * @throws NullPointerException if any argument is {@code null}
+     */
+    public final @NonNull RestconfFuture<Empty> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
+        final DataPath path;
+        try {
+            path = pathNormalizer.normalizeDataPath(apiPath);
+        } catch (RestconfDocumentedException e) {
+            return RestconfFuture.failed(e);
+        }
+
+        final NormalizedNode data;
+        try {
+            data = body.toNormalizedNode(new DataPutPath(databind, path.inference(), path.instance()));
+        } catch (RestconfDocumentedException e) {
+            return RestconfFuture.failed(e);
+        }
+
+        return merge(path.instance(), data);
+    }
+
+    public final @NonNull RestconfFuture<PatchStatusContext> dataPATCH(final ApiPath apiPath, final PatchBody body) {
+        final DataPath path;
+        try {
+            path = pathNormalizer.normalizeDataPath(apiPath);
+        } catch (RestconfDocumentedException e) {
+            return RestconfFuture.failed(e);
+        }
+
+        final PatchContext patch;
+        try {
+            patch = body.toPatchContext(new DataPatchPath(databind, path.instance()));
+        } catch (IOException e) {
+            LOG.debug("Error parsing YANG Patch input", e);
+            return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
+                ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, e));
+        }
+        return patchData(patch);
+    }
+
     /**
      * Process edit operations of one {@link PatchContext}.
      *
@@ -735,6 +760,31 @@ public abstract class RestconfStrategy {
         }
     }
 
+    /**
+     * Delete data from the configuration datastore. If the data does not exist, this operation will fail, as outlined
+     * in <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.7">RFC8040 section 4.7</a>
+     *
+     * @param apiPath Path to delete
+     * @return A {@link RestconfFuture}
+     * @throws NullPointerException if {@code apiPath} is {@code null}
+     */
+    @SuppressWarnings("checkstyle:abbreviationAsWordInName")
+    public final @NonNull RestconfFuture<Empty> dataDELETE(final ApiPath apiPath) {
+        final DataPath path;
+        try {
+            path = pathNormalizer.normalizeDataPath(apiPath);
+        } catch (RestconfDocumentedException e) {
+            return RestconfFuture.failed(e);
+        }
+
+        // FIXME: reject empty YangInstanceIdentifier, as datastores may not be deleted
+        final var ret = new SettableRestconfFuture<Empty>();
+        delete(ret, path.instance());
+        return ret;
+    }
+
+    abstract void delete(@NonNull SettableRestconfFuture<Empty> future, @NonNull YangInstanceIdentifier path);
+
     public final @NonNull RestconfFuture<NormalizedNodePayload> dataGET(final ApiPath apiPath,
             final DataGetParams params) {
         final DataPath path;
index 426a6983bf01eb93f38993639e6fbedb39512341..28613cdaa5da501e8228b23ff10a0cb5df6f0eed 100644 (file)
@@ -12,7 +12,6 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
-import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
 import java.net.URI;
@@ -34,7 +33,6 @@ import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
 import org.opendaylight.restconf.api.ApiPath;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfFuture;
-import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.nb.rfc8040.databind.ChildBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.DataPostBody;
@@ -44,13 +42,10 @@ import org.opendaylight.restconf.nb.rfc8040.databind.ResourceBody;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.MdsalRestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
-import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy.StrategyAndPath;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy.StrategyAndTail;
 import org.opendaylight.restconf.server.api.DataGetParams;
-import org.opendaylight.restconf.server.api.DataPatchPath;
 import org.opendaylight.restconf.server.api.DataPostResult;
 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
-import org.opendaylight.restconf.server.api.DataPutPath;
 import org.opendaylight.restconf.server.api.DataPutResult;
 import org.opendaylight.restconf.server.api.DatabindContext;
 import org.opendaylight.restconf.server.api.ModulesGetResult;
@@ -68,8 +63,6 @@ import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.common.YangNames;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
@@ -173,13 +166,13 @@ public final class MdsalRestconfServer
 
     @Override
     public RestconfFuture<Empty> dataDELETE(final ApiPath identifier) {
-        final StrategyAndPath stratAndPath;
+        final StrategyAndTail stratAndTail;
         try {
-            stratAndPath = localStrategy().resolveStrategyPath(identifier);
+            stratAndTail = localStrategy().resolveStrategy(identifier);
         } catch (RestconfDocumentedException e) {
             return RestconfFuture.failed(e);
         }
-        return stratAndPath.strategy().delete(stratAndPath.path().instance());
+        return stratAndTail.strategy().dataDELETE(stratAndTail.tail());
     }
 
     @Override
@@ -200,63 +193,34 @@ public final class MdsalRestconfServer
 
     @Override
     public RestconfFuture<Empty> dataPATCH(final ResourceBody body) {
-        final var strategy = localStrategy();
-        return dataPATCH(strategy, new DataPutPath(strategy.databind()), body);
+        return localStrategy().dataPATCH(ApiPath.empty(), body);
     }
 
     @Override
     public RestconfFuture<Empty> dataPATCH(final ApiPath identifier, final ResourceBody body) {
-        final StrategyAndPath strategyAndPath;
-        try {
-            strategyAndPath = localStrategy().resolveStrategyPath(identifier);
-        } catch (RestconfDocumentedException e) {
-            return RestconfFuture.failed(e);
-        }
-        final var strategy = strategyAndPath.strategy();
-        final var path = strategyAndPath.path();
-        return dataPATCH(strategy, new DataPutPath(strategy.databind(), path.inference(), path.instance()), body);
-    }
-
-    private static @NonNull RestconfFuture<Empty> dataPATCH(final RestconfStrategy strategy, final DataPutPath path,
-            final ResourceBody body) {
-        final NormalizedNode data;
+        final StrategyAndTail strategyAndTail;
         try {
-            data = body.toNormalizedNode(path);
+            strategyAndTail = localStrategy().resolveStrategy(identifier);
         } catch (RestconfDocumentedException e) {
             return RestconfFuture.failed(e);
         }
-        return strategy.merge(path.instance(), data);
+        return strategyAndTail.strategy().dataPATCH(strategyAndTail.tail(), body);
     }
 
     @Override
     public RestconfFuture<PatchStatusContext> dataPATCH(final PatchBody body) {
-        final var strategy = localStrategy();
-        return dataPATCH(strategy, new DataPatchPath(strategy.databind(), YangInstanceIdentifier.of()), body);
+        return localStrategy().dataPATCH(ApiPath.empty(), body);
     }
 
     @Override
     public RestconfFuture<PatchStatusContext> dataPATCH(final ApiPath identifier, final PatchBody body) {
-        final StrategyAndPath stratAndPath;
+        final StrategyAndTail strategyAndTail;
         try {
-            stratAndPath = localStrategy().resolveStrategyPath(identifier);
+            strategyAndTail = localStrategy().resolveStrategy(identifier);
         } catch (RestconfDocumentedException e) {
             return RestconfFuture.failed(e);
         }
-        final var strategy = stratAndPath.strategy();
-        return dataPATCH(strategy, new DataPatchPath(strategy.databind(), stratAndPath.path().instance()), body);
-    }
-
-    private static @NonNull RestconfFuture<PatchStatusContext> dataPATCH(final RestconfStrategy strategy,
-            final DataPatchPath path, final PatchBody body) {
-        final PatchContext patch;
-        try {
-            patch = body.toPatchContext(path);
-        } catch (IOException e) {
-            LOG.debug("Error parsing YANG Patch input", e);
-            return RestconfFuture.failed(new RestconfDocumentedException("Error parsing input: " + e.getMessage(),
-                ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE, e));
-        }
-        return strategy.patchData(patch);
+        return strategyAndTail.strategy().dataPATCH(strategyAndTail.tail(), body);
     }
 
     @Override
index 7ddeaaf5adb393f45f1a844acb13f2bf0d5fa1f6..2c089f158b128a156075812d0b1e37d6764c492f 100644 (file)
@@ -27,6 +27,7 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.opendaylight.restconf.api.ApiPath;
 import org.opendaylight.restconf.api.query.ContentParam;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.patch.PatchContext;
@@ -236,7 +237,7 @@ abstract class AbstractRestconfStrategyTest extends AbstractJukeboxTest {
      */
     @Test
     public final void testDeleteData() throws Exception {
-        final var future = testDeleteDataStrategy().delete(YangInstanceIdentifier.of());
+        final var future = testDeleteDataStrategy().dataDELETE(ApiPath.empty());
         assertNotNull(Futures.getDone(future));
     }
 
@@ -247,7 +248,7 @@ abstract class AbstractRestconfStrategyTest extends AbstractJukeboxTest {
      */
     @Test
     public final void testNegativeDeleteData() {
-        final var future = testNegativeDeleteDataStrategy().delete(YangInstanceIdentifier.of());
+        final var future = testNegativeDeleteDataStrategy().dataDELETE(ApiPath.empty());
         final var ex = assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
         assertThat(ex, instanceOf(RestconfDocumentedException.class));
         final var errors = ((RestconfDocumentedException) ex).getErrors();