Allow dataPUT() to control ETag/Last-Modified 57/109157/3
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Dec 2023 08:15:43 +0000 (09:15 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 6 Dec 2023 11:09:29 +0000 (12:09 +0100)
When creating or replacing a data resource, the underlying strategy
should be free to indicate ETag/Last-Modified headers.

Allow this information to be communicated via DataPutResult and pick it
up when generating a Response.

JIRA: NETCONF-1207
Change-Id: I86b726fcaa5bfb7d762483f007d6de77b894f802
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/DataPutResult.java
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/RestconfServer.java

index 1b7d8b90ee26f676938c1f3004060fce00069220..d593a6ccbd34f0bd09665704dfd2f237dbb3dd56 100644 (file)
@@ -608,11 +608,10 @@ public final class JaxRsRestconf implements ParamConverterProvider {
         future.addCallback(new JaxRsRestconfCallback<>(ar) {
             @Override
             Response transform(final DataPutResult result) {
-                return switch (result) {
-                    // Note: no Location header, as it matches the request path
-                    case CREATED -> Response.status(Status.CREATED).build();
-                    case REPLACED -> Response.noContent().build();
-                };
+                // Note: no Location header, as it matches the request path
+                final var builder = result.created() ? Response.status(Status.CREATED) : Response.noContent();
+                fillConfigurationMetadata(builder, result);
+                return builder.build();
             }
         });
     }
index 6c2de8a46d7d9dc89fc4d2a0e7e62182aa256f0f..a1313afcd0b2b8ca279e64e810385cb8dd0521af 100644 (file)
@@ -179,6 +179,8 @@ 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 final @NonNull ImmutableMap<QName, RpcImplementation> localRpcs;
     private final @NonNull ApiPathNormalizer pathNormalizer;
@@ -376,7 +378,7 @@ public abstract class RestconfStrategy {
         Futures.addCallback(commitFuture, new FutureCallback<CommitInfo>() {
             @Override
             public void onSuccess(final CommitInfo result) {
-                ret.set(exists ? DataPutResult.REPLACED : DataPutResult.CREATED);
+                ret.set(exists ? PUT_REPLACED : PUT_CREATED);
             }
 
             @Override
index 019b0476ccb0192d16c482efe92921e72e3f0c3c..7ff715e7f97e537011c97f3f897f9cee9374fce1 100644 (file)
@@ -7,18 +7,23 @@
  */
 package org.opendaylight.restconf.server.api;
 
+import java.time.Instant;
+import org.eclipse.jdt.annotation.Nullable;
+
 /**
  * Result of a {@code PUT} request as defined in
  * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.5">RFC8040 section 4.5</a>. The definition makes it
  * clear that the logical operation is {@code create-or-replace}.
+ *
+ * @param created {@code true} if the resource was created, {@code false} if it was replaced
+ * @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 enum DataPutResult {
-    /**
-     * A new resource has been created.
-     */
-    CREATED,
-    /*
-     * An existing resources has been replaced.
-     */
-    REPLACED;
+public record DataPutResult(
+        boolean created,
+        @Nullable EntityTag entityTag,
+        @Nullable Instant lastModified) implements ConfigurationMetadata {
+    public DataPutResult(final boolean created) {
+        this(created, null, null);
+    }
 }
\ No newline at end of file
index 5b269718eea4e69c0eba5b1f095033dfdf716bef..c900e24eec9c53aaedc223eec513d21f88d100e4 100644 (file)
@@ -26,8 +26,6 @@ import org.opendaylight.yangtools.yang.common.Empty;
  * An implementation of a RESTCONF server, implementing the
  * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.3">RESTCONF API Resource</a>.
  */
-// FIXME: NETCONF-1207: configuration datastore should maintain ETag and Last-Modified headers, so that these can be
-//                      returned when PATCH/PUT modify the data.
 @NonNullByDefault
 public interface RestconfServer {
     /**
@@ -63,6 +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);
 
     /**
@@ -73,6 +72,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);
 
     /**
@@ -82,6 +82,7 @@ public interface RestconfServer {
      * @param body YANG Patch body
      * @return A {@link RestconfFuture} of the {@link PatchStatusContext} content
      */
+    // FIXME: NETCONF-1207: result should carry ConfigurationMetadata
     RestconfFuture<PatchStatusContext> dataPATCH(PatchBody body);
 
     /**
@@ -92,6 +93,7 @@ public interface RestconfServer {
      * @param body YANG Patch body
      * @return A {@link RestconfFuture} of the {@link PatchStatusContext} content
      */
+    // FIXME: NETCONF-1207: result should carry ConfigurationMetadata
     RestconfFuture<PatchStatusContext> dataPATCH(ApiPath identifier, PatchBody body);
 
     RestconfFuture<DataPostResult.CreateResource> dataPOST(ChildBody body, Map<String, String> queryParameters);