Turn ApiPath into a record
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / transactions / RestconfStrategy.java
index 6eb129bad97429b5ea32fefff68f9c727387563b..cb175b55d4fcd267f77e721f27b1386535161474 100644 (file)
@@ -91,10 +91,10 @@ import org.opendaylight.restconf.server.api.OperationsPostResult;
 import org.opendaylight.restconf.server.api.PatchBody;
 import org.opendaylight.restconf.server.api.ResourceBody;
 import org.opendaylight.restconf.server.spi.ApiPathNormalizer;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.DataPath;
 import org.opendaylight.restconf.server.spi.ApiPathNormalizer.InstanceReference;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.OperationPath;
-import org.opendaylight.restconf.server.spi.ApiPathNormalizer.OperationPath.Rpc;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Action;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Data;
+import org.opendaylight.restconf.server.spi.ApiPathNormalizer.Path.Rpc;
 import org.opendaylight.restconf.server.spi.OperationInput;
 import org.opendaylight.restconf.server.spi.RpcImplementation;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.with.defaults.rev110601.WithDefaultsMode;
@@ -158,7 +158,7 @@ import org.slf4j.LoggerFactory;
 //        operations. This should be handled through proper allocation indirection.
 public abstract class RestconfStrategy {
     @NonNullByDefault
-    public record StrategyAndPath(RestconfStrategy strategy, DataPath path) {
+    public record StrategyAndPath(RestconfStrategy strategy, Data path) {
         public StrategyAndPath {
             requireNonNull(strategy);
             requireNonNull(path);
@@ -333,7 +333,7 @@ public abstract class RestconfStrategy {
 
     public @NonNull RestconfFuture<DataPutResult> dataPUT(final ApiPath apiPath, final ResourceBody body,
             final Map<String, String> queryParameters) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -504,9 +504,8 @@ public abstract class RestconfStrategy {
             final NormalizedNode data, final @Nullable Insert insert) {
         final ListenableFuture<? extends CommitInfo> future;
         if (insert != null) {
-            final var parentPath = path.coerceParent();
-            checkListAndOrderedType(parentPath);
-            future = insertAndCommitPost(path, data, insert, parentPath);
+            checkListAndOrderedType(path);
+            future = insertAndCommitPost(path, data, insert);
         } else {
             future = createAndCommit(prepareWriteExecution(), path, data);
         }
@@ -530,41 +529,40 @@ public abstract class RestconfStrategy {
     }
 
     private ListenableFuture<? extends CommitInfo> insertAndCommitPost(final YangInstanceIdentifier path,
-            final NormalizedNode data, final @NonNull Insert insert, final YangInstanceIdentifier parent) {
-        final var grandParent = parent.coerceParent();
+            final NormalizedNode data, final @NonNull Insert insert) {
         final var tx = prepareWriteExecution();
 
         return switch (insert.insert()) {
             case FIRST -> {
-                final var readData = tx.readList(grandParent);
+                final var readData = tx.readList(path);
                 if (readData == null || readData.isEmpty()) {
                     tx.replace(path, data);
                 } else {
-                    checkItemDoesNotExists(exists(path), path);
-                    tx.remove(grandParent);
+                    checkListDataDoesNotExist(path, data);
+                    tx.remove(path);
                     tx.replace(path, data);
-                    tx.replace(grandParent, readData);
+                    tx.replace(path, readData);
                 }
                 yield tx.commit();
             }
             case LAST -> createAndCommit(tx, path, data);
             case BEFORE -> {
-                final var readData = tx.readList(grandParent);
+                final var readData = tx.readList(path);
                 if (readData == null || readData.isEmpty()) {
                     tx.replace(path, data);
                 } else {
-                    checkItemDoesNotExists(exists(path), path);
-                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, grandParent, true);
+                    checkListDataDoesNotExist(path, data);
+                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, true);
                 }
                 yield tx.commit();
             }
             case AFTER -> {
-                final var readData = tx.readList(grandParent);
+                final var readData = tx.readList(path);
                 if (readData == null || readData.isEmpty()) {
                     tx.replace(path, data);
                 } else {
-                    checkItemDoesNotExists(exists(path), path);
-                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, grandParent, false);
+                    checkListDataDoesNotExist(path, data);
+                    insertWithPointPost(tx, path, data, verifyNotNull(insert.pointArg()), readData, false);
                 }
                 yield tx.commit();
             }
@@ -581,7 +579,7 @@ public abstract class RestconfStrategy {
      * @throws NullPointerException if any argument is {@code null}
      */
     public final @NonNull RestconfFuture<DataPatchResult> dataPATCH(final ApiPath apiPath, final ResourceBody body) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -599,7 +597,7 @@ public abstract class RestconfStrategy {
     }
 
     public final @NonNull RestconfFuture<DataYangPatchResult> dataPATCH(final ApiPath apiPath, final PatchBody body) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -722,8 +720,8 @@ public abstract class RestconfStrategy {
 
     private void insertWithPointPost(final RestconfTransaction tx, final YangInstanceIdentifier path,
             final NormalizedNode data, final PathArgument pointArg, final NormalizedNodeContainer<?> readList,
-            final YangInstanceIdentifier grandParentPath, final boolean before) {
-        tx.remove(grandParentPath);
+            final boolean before) {
+        tx.remove(path);
 
         int lastItemPosition = 0;
         for (var nodeChild : readList.body()) {
@@ -737,13 +735,11 @@ public abstract class RestconfStrategy {
         }
 
         int lastInsertedPosition = 0;
-        final var emptySubtree = fromInstanceId(modelContext(), grandParentPath);
-        tx.merge(YangInstanceIdentifier.of(emptySubtree.name()), emptySubtree);
         for (var nodeChild : readList.body()) {
             if (lastInsertedPosition == lastItemPosition) {
                 tx.replace(path, data);
             }
-            tx.replace(grandParentPath.node(nodeChild.name()), nodeChild);
+            tx.replace(path.node(nodeChild.name()), nodeChild);
             lastInsertedPosition++;
         }
 
@@ -768,6 +764,23 @@ public abstract class RestconfStrategy {
         return tx.commit();
     }
 
+    /**
+     * Check if child items do NOT already exists in List at specified {@code path}.
+     *
+     * @param data Data to be checked
+     * @param path Path to be checked
+     * @throws RestconfDocumentedException if data already exists.
+     */
+    private void checkListDataDoesNotExist(final YangInstanceIdentifier path, final NormalizedNode data) {
+        if (data instanceof NormalizedNodeContainer<?> dataNode) {
+            for (final var node : dataNode.body()) {
+                checkItemDoesNotExists(exists(path.node(node.name())), path.node(node.name()));
+            }
+        } else {
+            throw new RestconfDocumentedException("Unexpected node type: " + data.getClass().getName());
+        }
+    }
+
     /**
      * Check if items do NOT already exists at specified {@code path}.
      *
@@ -794,7 +807,7 @@ public abstract class RestconfStrategy {
      */
     @SuppressWarnings("checkstyle:abbreviationAsWordInName")
     public final @NonNull RestconfFuture<Empty> dataDELETE(final ApiPath apiPath) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -811,7 +824,7 @@ public abstract class RestconfStrategy {
 
     public final @NonNull RestconfFuture<DataGetResult> dataGET(final ApiPath apiPath,
             final DataGetParams params) {
-        final DataPath path;
+        final Data path;
         try {
             path = pathNormalizer.normalizeDataPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -820,7 +833,7 @@ public abstract class RestconfStrategy {
         return dataGET(path, params);
     }
 
-    abstract @NonNull RestconfFuture<DataGetResult> dataGET(DataPath path, DataGetParams params);
+    abstract @NonNull RestconfFuture<DataGetResult> dataGET(Data path, DataGetParams params);
 
     static final @NonNull RestconfFuture<DataGetResult> completeDataGET(final Inference inference,
             final QueryParameters queryParams, final @Nullable NormalizedNode node,
@@ -1033,22 +1046,15 @@ public abstract class RestconfStrategy {
 
     static final NormalizedNode mergeConfigAndSTateDataIfNeeded(final NormalizedNode stateDataNode,
             final NormalizedNode configDataNode) {
-        // if no data exists
-        if (stateDataNode == null && configDataNode == null) {
-            return null;
-        }
-
-        // return config data
         if (stateDataNode == null) {
+            // No state, return config
             return configDataNode;
         }
-
-        // return state data
         if (configDataNode == null) {
+            // No config, return state
             return stateDataNode;
         }
-
-        // merge data from config and state
+        // merge config and state
         return mergeStateAndConfigData(stateDataNode, configDataNode);
     }
 
@@ -1250,8 +1256,8 @@ public abstract class RestconfStrategy {
                 .collect(ImmutableSet.toImmutableSet());
             if (!rpcNames.isEmpty()) {
                 final var namespace = entry.getKey();
-                table.computeIfAbsent(namespace.getNamespace(), ignored -> new HashMap<>())
-                    .put(namespace.getRevision().orElse(null), rpcNames);
+                table.computeIfAbsent(namespace.namespace(), ignored -> new HashMap<>())
+                    .put(namespace.revision(), rpcNames);
             }
         }
 
@@ -1261,7 +1267,7 @@ public abstract class RestconfStrategy {
             entry.getValue().entrySet().stream()
             .sorted(Comparator.comparing(Entry::getKey, (first, second) -> Revision.compare(second, first)))
             .findFirst()
-            .ifPresent(row -> rpcs.putAll(QNameModule.create(entry.getKey(), row.getKey()), row.getValue()));
+            .ifPresent(row -> rpcs.putAll(QNameModule.of(entry.getKey(), row.getKey()), row.getValue()));
         }
         return RestconfFuture.of(new OperationsGetResult.Container(modelContext, rpcs.build()));
     }
@@ -1283,7 +1289,7 @@ public abstract class RestconfStrategy {
 
     public @NonNull RestconfFuture<OperationsPostResult> operationsPOST(final URI restconfURI, final ApiPath apiPath,
             final OperationInputBody body) {
-        final OperationPath.Rpc path;
+        final Rpc path;
         try {
             path = pathNormalizer.normalizeRpcPath(apiPath);
         } catch (RestconfDocumentedException e) {
@@ -1409,13 +1415,13 @@ public abstract class RestconfStrategy {
         } catch (RestconfDocumentedException e) {
             return RestconfFuture.failed(e);
         }
-        if (path instanceof DataPath dataPath) {
+        if (path instanceof Data dataPath) {
             try (var resourceBody = body.toResource()) {
                 return dataCreatePOST(new DataPostPath(databind, dataPath.inference(), dataPath.instance()),
                     resourceBody, queryParameters);
             }
         }
-        if (path instanceof OperationPath.Action actionPath) {
+        if (path instanceof Action actionPath) {
             try (var inputBody = body.toOperationInput()) {
                 return dataInvokePOST(actionPath, inputBody);
             }
@@ -1454,8 +1460,7 @@ public abstract class RestconfStrategy {
         return ret;
     }
 
-    private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final OperationPath.Action path,
-            final OperationInputBody body) {
+    private @NonNull RestconfFuture<InvokeOperation> dataInvokePOST(final Action path, final OperationInputBody body) {
         final var inference = path.inference();
         final ContainerNode input;
         try {
@@ -1487,12 +1492,12 @@ public abstract class RestconfStrategy {
      * @return {@link DOMActionResult}
      */
     private static RestconfFuture<DOMActionResult> dataInvokePOST(final DOMActionService actionService,
-            final OperationPath.Action path, final @NonNull ContainerNode input) {
+            final Action path, final @NonNull ContainerNode input) {
         final var ret = new SettableRestconfFuture<DOMActionResult>();
 
         Futures.addCallback(actionService.invokeAction(
             path.inference().toSchemaInferenceStack().toSchemaNodeIdentifier(),
-            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, path.instance()), input),
+            DOMDataTreeIdentifier.of(LogicalDatastoreType.OPERATIONAL, path.instance()), input),
             new FutureCallback<DOMActionResult>() {
                 @Override
                 public void onSuccess(final DOMActionResult result) {