Add DataPostResult 17/109017/4
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 17 Nov 2023 23:44:19 +0000 (00:44 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 18 Nov 2023 14:29:24 +0000 (15:29 +0100)
POST to /data can result in multiple different results. Add
DataPostResult encapsulate the possiblities.

Also introduce CreateResource, which is the result of POST in Operation
Create Mode -- moving some of the worries away from JAX-RS layer's
postData().

JIRA: NETCONF-773
Change-Id: I56dcde72630d53290f002e9b6258924175bb3072
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.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/DataPostResult.java [new file with mode: 0644]

index 03d9ca7283fa7e21323c9cfb37b958f33c7f1c0c..078e7176711c8b0a74df84f4250c5c8666326d30 100644 (file)
@@ -10,7 +10,6 @@ package org.opendaylight.restconf.nb.rfc8040.rests.services.impl;
 import static java.util.Objects.requireNonNull;
 
 import java.io.InputStream;
-import java.net.URI;
 import java.util.List;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.Encoded;
@@ -38,15 +37,10 @@ import org.opendaylight.restconf.nb.rfc8040.databind.XmlOperationInputBody;
 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
 import org.opendaylight.restconf.nb.rfc8040.legacy.InstanceIdentifierContext;
 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
-import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
-import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
 
 /**
@@ -178,8 +172,9 @@ public final class RestconfDataServiceImpl {
 
         strategy.postData(path, data, insert).addCallback(new JaxRsRestconfCallback<>(ar) {
             @Override
-            Response transform(final Empty result) {
-                return Response.created(resolveLocation(uriInfo, path, modelContext, data)).build();
+            Response transform(final CreateResource result) {
+                return Response.created(uriInfo.getBaseUriBuilder().path("data").path(result.createdPath()).build())
+                    .build();
             }
         });
     }
@@ -192,27 +187,6 @@ public final class RestconfDataServiceImpl {
         return ret;
     }
 
-    /**
-     * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
-     *
-     * @param uriInfo       uri info
-     * @param initialPath   data path
-     * @param schemaContext reference to {@link SchemaContext}
-     * @return {@link URI}
-     */
-    private static URI resolveLocation(final UriInfo uriInfo, final YangInstanceIdentifier initialPath,
-                                       final EffectiveModelContext schemaContext, final NormalizedNode data) {
-        YangInstanceIdentifier path = initialPath;
-        if (data instanceof MapNode mapData) {
-            final var children = mapData.body();
-            if (!children.isEmpty()) {
-                path = path.node(children.iterator().next().name());
-            }
-        }
-
-        return uriInfo.getBaseUriBuilder().path("data").path(IdentifierCodec.serialize(path, schemaContext)).build();
-    }
-
     /**
      * Invoke Action operation.
      *
index c491b4d5879161976d6cdbd57b2d5ef07d5e5abb..dc00ea71746de94e8c63d4c41ac0f558efec4731 100644 (file)
@@ -44,6 +44,8 @@ import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.restconf.nb.rfc8040.Insert;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
+import org.opendaylight.restconf.server.api.DataPostResult.CreateResource;
 import org.opendaylight.restconf.server.api.DataPutResult;
 import org.opendaylight.restconf.server.spi.OperationInput;
 import org.opendaylight.restconf.server.spi.OperationOutput;
@@ -364,8 +366,8 @@ public abstract class RestconfStrategy {
      * @param insert  {@link Insert}
      * @return A {@link RestconfFuture}
      */
-    public final RestconfFuture<Empty> postData(final YangInstanceIdentifier path, final NormalizedNode data,
-            final @Nullable Insert insert) {
+    public final RestconfFuture<CreateResource> postData(final YangInstanceIdentifier path,
+            final NormalizedNode data, final @Nullable Insert insert) {
         final ListenableFuture<? extends CommitInfo> future;
         if (insert != null) {
             final var parentPath = path.coerceParent();
@@ -374,20 +376,23 @@ public abstract class RestconfStrategy {
         } else {
             future = createAndCommit(prepareWriteExecution(), path, data);
         }
-        final var ret = new SettableRestconfFuture<Empty>();
 
+        final var ret = new SettableRestconfFuture<CreateResource>();
         Futures.addCallback(future, new FutureCallback<CommitInfo>() {
             @Override
             public void onSuccess(final CommitInfo result) {
-                ret.set(Empty.value());
+                ret.set(new CreateResource(IdentifierCodec.serialize(
+                    data instanceof MapNode mapData && !mapData.isEmpty()
+                        ? path.node(mapData.body().iterator().next().name()) : path,
+                    modelContext)));
             }
 
             @Override
             public void onFailure(final Throwable cause) {
                 ret.setFailure(TransactionUtil.decodeException(cause, "POST", path, modelContext));
             }
-        }, MoreExecutors.directExecutor());
 
+        }, MoreExecutors.directExecutor());
         return ret;
     }
 
diff --git a/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/DataPostResult.java b/restconf/restconf-nb/src/main/java/org/opendaylight/restconf/server/api/DataPostResult.java
new file mode 100644 (file)
index 0000000..3e139a0
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Result of a {@code POST} request as defined in
+ * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.4">RFC8040 section 4.4</a>.
+ */
+@NonNullByDefault
+public sealed interface DataPostResult {
+    /**
+     * Result of a {@code POST} request as defined in
+     * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-4.4.1">RFC8040 Create ResourceMode</a>.
+     *
+     * @param createdPath API path of the newly-created resource
+     */
+    // FIXME: use ApiPath instead of String
+    record CreateResource(String createdPath) implements DataPostResult {
+        public CreateResource {
+            requireNonNull(createdPath);
+        }
+    }
+}