Bug 6947 / Bug 6948 - implement point and insert query params
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / restful / utils / PutDataTransactionUtil.java
index 8db0b433f40507cce4d20c9aa48c018575a19b9a..19fb343d11e39c40ed9e86bdfde21c61b6ebed7f 100644 (file)
@@ -14,8 +14,11 @@ import java.util.Map;
 import javax.ws.rs.core.Response;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
@@ -27,8 +30,18 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
@@ -131,15 +144,20 @@ public final class PutDataTransactionUtil {
      * @param schemaCtxRef
      *            - reference to {@link SchemaContext}
      * @param transactionNode
+     *            - wrapper of variables for transaction
+     * @param point
+     *            - query parameter
+     * @param insert
+     *            - query parameter
      * @return {@link CheckedFuture}
      */
     public static Response putData(final NormalizedNodeContext payload,
-            final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode) {
+            final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode, final String insert, final String point) {
         final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
         final ResponseFactory responseFactory = new ResponseFactory(
                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode));
         final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaCtxRef.get(),
-                transactionNode.getTransactionChain().newWriteOnlyTransaction(), payload.getData());
+                transactionNode.getTransactionChain(), payload.getData(), insert, point);
         FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
         return responseFactory.build();
     }
@@ -151,17 +169,241 @@ public final class PutDataTransactionUtil {
      *            - path of data
      * @param schemaContext
      *            - {@link SchemaContext}
-     * @param writeTx
+     * @param domTransactionChain
      *            - write transaction
      * @param data
      *            - data
+     * @param point
+     *            - query parameter
+     * @param insert
+     *            - query parameter
      * @return {@link CheckedFuture}
      */
     private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
-            final SchemaContext schemaContext,
-            final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
+            final SchemaContext schemaContext, final DOMTransactionChain domTransactionChain,
+            final NormalizedNode<?, ?> data, final String insert, final String point) {
+        final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
+        if (insert == null) {
+            return makePut(path, schemaContext, newReadWriteTransaction, data);
+        } else {
+            final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+            switch (insert) {
+                case "first":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                            simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction, schemaContext, data);
+                            listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction, schemaContext,
+                                    readList);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                            simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction, schemaContext, data);
+                            listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction, schemaContext,
+                                    readLeafList);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                case "last":
+                    return makePut(path, schemaContext, newReadWriteTransaction, data);
+                case "before":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readList, true);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readLeafList, true);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readList, false);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readLeafList, true);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    public static NormalizedNode<?, ?> readList(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+            final DOMTransactionChain domTransactionChain, final DataSchemaNode schemaNode) {
+        final InstanceIdentifierContext<?> iid = new InstanceIdentifierContext<SchemaNode>(
+                path.getParent(), schemaNode, null, schemaContext);
+        final TransactionVarsWrapper transactionNode =
+                new TransactionVarsWrapper(iid, null, domTransactionChain);
+        final NormalizedNode<?, ?> readData = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode);
+        return readData;
+    }
+
+    private static void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
+            final OrderedLeafSetNode<?> readLeafList, final boolean before) {
+        rWTransaction.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, rWTransaction, schemaContext, data);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private static void insertWithPointListPut(final DOMDataReadWriteTransaction writeTx,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
+            final OrderedMapNode readList, final boolean before) {
+        writeTx.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, writeTx, schemaContext, data);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+            writeTx.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
+
+    private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+            final OrderedLeafSetNode<?> payload) {
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
+            final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+            writeTx.put(datastore, childPath, child);
+        }
+    }
+
+    private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+            final OrderedMapNode payload) {
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        for (final MapEntryNode child : ((MapNode) payload).getValue()) {
+            final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+            writeTx.put(datastore, childPath, child);
+        }
+    }
+
+    private static void simplePut(final LogicalDatastoreType configuration, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+            final NormalizedNode<?, ?> data) {
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+    }
+
+    private static CheckedFuture<Void, TransactionCommitFailedException> makePut(final YangInstanceIdentifier path,
+            final SchemaContext schemaContext, final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
         TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
         writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
         return writeTx.submit();
     }
+
+    public static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
+        final YangInstanceIdentifier parent = path.getParent();
+        final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
+        final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
+
+        if (dataSchemaNode instanceof ListSchemaNode) {
+            if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
+            }
+            return dataSchemaNode;
+        }
+        if (dataSchemaNode instanceof LeafListSchemaNode) {
+            if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException(
+                        "Insert parameter can be used only with ordered-by user leaf-list.");
+            }
+            return dataSchemaNode;
+        }
+        throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
+    }
 }