Allow plain PATCH to communicate ETag/Last-Modified 62/109162/2
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Dec 2023 10:49:56 +0000 (11:49 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Dec 2023 11:09:29 +0000 (12:09 +0100)
PATCH methods should communicate ETag/Last-Modified where applicable.
Introduce DataPatchResult to communicate these for plain PATCH.

JIRA: NETCONF-1207
Change-Id: I3ed2e80385f2b5acdd90c04adab49b491ebd5a79
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/jaxrs/JaxRsRestconf.java
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/api/DataPatchResult.java [new file with mode: 0644]
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/RestconfServer.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/mdsal/MdsalRestconfServer.java

index d593a6ccbd34f0bd09665704dfd2f237dbb3dd56..f4f052f263dc8bcb90654d267f0dfaf541719485 100644 (file)
@@ -67,6 +67,7 @@ import org.opendaylight.restconf.nb.rfc8040.legacy.ErrorTags;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
 import org.opendaylight.restconf.server.api.ConfigurationMetadata;
 import org.opendaylight.restconf.server.api.DataGetResult;
+import org.opendaylight.restconf.server.api.DataPatchResult;
 import org.opendaylight.restconf.server.api.DataPostResult;
 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
 import org.opendaylight.restconf.server.api.DataPostResult.InvokeOperation;
@@ -292,11 +293,13 @@ public final class JaxRsRestconf implements ParamConverterProvider {
         }
     }
 
-    private static void completeDataPATCH(final RestconfFuture<Empty> future, final AsyncResponse ar) {
+    private static void completeDataPATCH(final RestconfFuture<DataPatchResult> future, final AsyncResponse ar) {
         future.addCallback(new JaxRsRestconfCallback<>(ar) {
             @Override
-            Response transform(final Empty result) {
-                return Response.ok().build();
+            Response transform(final DataPatchResult result) {
+                final var builder = Response.ok();
+                fillConfigurationMetadata(builder, result);
+                return builder.build();
             }
         });
     }
index a1313afcd0b2b8ca279e64e810385cb8dd0521af..196679cf6be67fe488e001c190ec6798a20d6cca 100644 (file)
@@ -77,6 +77,7 @@ import org.opendaylight.restconf.server.api.ConfigurationMetadata;
 import org.opendaylight.restconf.server.api.DataGetParams;
 import org.opendaylight.restconf.server.api.DataGetResult;
 import org.opendaylight.restconf.server.api.DataPatchPath;
+import org.opendaylight.restconf.server.api.DataPatchResult;
 import org.opendaylight.restconf.server.api.DataPostPath;
 import org.opendaylight.restconf.server.api.DataPostResult;
 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
@@ -181,6 +182,7 @@ public abstract class RestconfStrategy {
     private static final Logger LOG = LoggerFactory.getLogger(RestconfStrategy.class);
     private static final @NonNull DataPutResult PUT_CREATED = new DataPutResult(true);
     private static final @NonNull DataPutResult PUT_REPLACED = new DataPutResult(false);
+    private static final @NonNull DataPatchResult PATCH_EMPTY = new DataPatchResult();
 
     private final @NonNull ImmutableMap<QName, RpcImplementation> localRpcs;
     private final @NonNull ApiPathNormalizer pathNormalizer;
@@ -302,14 +304,14 @@ public abstract class RestconfStrategy {
     abstract ListenableFuture<Boolean> exists(YangInstanceIdentifier path);
 
     @VisibleForTesting
-    final @NonNull RestconfFuture<Empty> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
-        final var ret = new SettableRestconfFuture<Empty>();
+    final @NonNull RestconfFuture<DataPatchResult> merge(final YangInstanceIdentifier path, final NormalizedNode data) {
+        final var ret = new SettableRestconfFuture<DataPatchResult>();
         merge(ret, requireNonNull(path), requireNonNull(data));
         return ret;
     }
 
-    private void merge(final @NonNull SettableRestconfFuture<Empty> future, final @NonNull YangInstanceIdentifier path,
-            final @NonNull NormalizedNode data) {
+    private void merge(final @NonNull SettableRestconfFuture<DataPatchResult> future,
+            final @NonNull YangInstanceIdentifier path, final @NonNull NormalizedNode data) {
         final var tx = prepareWriteExecution();
         // FIXME: this method should be further specialized to eliminate this call -- it is only needed for MD-SAL
         tx.ensureParentsByMerge(path);
@@ -317,7 +319,8 @@ public abstract class RestconfStrategy {
         Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
             @Override
             public void onSuccess(final CommitInfo result) {
-                future.set(Empty.value());
+                // TODO: extract details once CommitInfo can communicate them
+                future.set(PATCH_EMPTY);
             }
 
             @Override
@@ -569,7 +572,7 @@ public abstract class RestconfStrategy {
      * @return A {@link RestconfFuture}
      * @throws NullPointerException if any argument is {@code null}
      */
-    public final @NonNull RestconfFuture<Empty> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
+    public final @NonNull RestconfFuture<DataPatchResult> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
         final DataPath path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/DataPatchResult.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/DataPatchResult.java
new file mode 100644 (file)
index 0000000..af1d88c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 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.server.api;
+
+import java.time.Instant;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Result of a {@code PATCH} request as defined in
+ * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.6.1">RFC8040, section 4.6.1</a>.
+ *
+ * @param entityTag response {@code ETag} header, or {@code null} if not applicable
+ * @param lastModified response {@code Last-Modified} header, or {@code null} if not applicable
+ */
+public record DataPatchResult(
+        @Nullable EntityTag entityTag,
+        @Nullable Instant lastModified) implements ConfigurationMetadata {
+    public DataPatchResult() {
+        this(null, null);
+    }
+}
index c900e24eec9c53aaedc223eec513d21f88d100e4..590567c8f3f86c62736848c354093643716444b6 100644 (file)
@@ -61,8 +61,7 @@ public interface RestconfServer {
      * @param body data node for put to config DS
      * @return A {@link RestconfFuture} of the operation
      */
-    // FIXME: NETCONF-1207: result should carry ConfigurationMetadata
-    RestconfFuture<Empty> dataPATCH(ResourceBody body);
+    RestconfFuture<DataPatchResult> dataPATCH(ResourceBody body);
 
     /**
      * Partially modify the target data resource, as defined in
@@ -72,8 +71,7 @@ public interface RestconfServer {
      * @param body data node for put to config DS
      * @return A {@link RestconfFuture} of the operation
      */
-    // FIXME: NETCONF-1207: result should carry ConfigurationMetadata
-    RestconfFuture<Empty> dataPATCH(ApiPath identifier, ResourceBody body);
+    RestconfFuture<DataPatchResult> dataPATCH(ApiPath identifier, ResourceBody body);
 
     /**
      * Ordered list of edits that are applied to the datastore by the server, as defined in
index 1dc8fadc8a7f01915c2838ee9feff132f001c9d7..3e2a8346e280ac615b6a65f8f44c5e0d304472fc 100644 (file)
@@ -45,6 +45,7 @@ import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy.StrategyAndTail;
 import org.opendaylight.restconf.server.api.DataGetParams;
 import org.opendaylight.restconf.server.api.DataGetResult;
+import org.opendaylight.restconf.server.api.DataPatchResult;
 import org.opendaylight.restconf.server.api.DataPostResult;
 import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
 import org.opendaylight.restconf.server.api.DataPutResult;
@@ -193,12 +194,12 @@ public final class MdsalRestconfServer
     }
 
     @Override
-    public RestconfFuture<Empty> dataPATCH(final ResourceBody body) {
+    public RestconfFuture<DataPatchResult> dataPATCH(final ResourceBody body) {
         return localStrategy().dataPATCH(ApiPath.empty(), body);
     }
 
     @Override
-    public RestconfFuture<Empty> dataPATCH(final ApiPath identifier, final ResourceBody body) {
+    public RestconfFuture<DataPatchResult> dataPATCH(final ApiPath identifier, final ResourceBody body) {
         final StrategyAndTail strategyAndTail;
         try {
             strategyAndTail = localStrategy().resolveStrategy(identifier);