6041081dba03eaa0f8df669115090e7824e583b1
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / PostDataTransactionUtil.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
9
10 import com.google.common.util.concurrent.FluentFuture;
11 import java.net.URI;
12 import java.util.Optional;
13 import javax.ws.rs.core.Response;
14 import javax.ws.rs.core.Response.Status;
15 import javax.ws.rs.core.UriBuilder;
16 import javax.ws.rs.core.UriInfo;
17 import org.opendaylight.mdsal.common.api.CommitInfo;
18 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
20 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
21 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
22 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
23 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
24 import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
25 import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
26 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
27 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
35 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
39
40 /**
41  * Util class to post data to DS.
42  *
43  */
44 public final class PostDataTransactionUtil {
45     private PostDataTransactionUtil() {
46         throw new UnsupportedOperationException("Util class.");
47     }
48
49     /**
50      * Check mount point and prepare variables for post data.
51      *
52      * @param uriInfo
53      *
54      * @param payload
55      *             data
56      * @param transactionNode
57      *             wrapper for transaction data
58      * @param schemaContextRef
59      *             reference to actual {@link SchemaContext}
60      * @param point
61      *             point
62      * @param insert
63      *             insert
64      * @return {@link Response}
65      */
66     public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
67             final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert,
68             final String point) {
69         final FluentFuture<? extends CommitInfo> future = submitData(
70                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
71                 transactionNode, schemaContextRef.get(), insert, point);
72         final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
73         final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
74         FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
75         return dataFactory.build();
76     }
77
78     /**
79      * Post data by type.
80      *
81      * @param path
82      *             path
83      * @param data
84      *             data
85      * @param transactionNode
86      *             wrapper for data to transaction
87      * @param schemaContext
88      *             schema context of data
89      * @param point
90      *             query parameter
91      * @param insert
92      *             query parameter
93      * @return {@link FluentFuture}
94      */
95     private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
96             final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
97             final SchemaContext schemaContext, final String insert, final String point) {
98         final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain();
99         final DOMDataTreeReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
100         if (insert == null) {
101             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(), newReadWriteTransaction);
102             return newReadWriteTransaction.commit();
103         } else {
104             final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
105             switch (insert) {
106                 case "first":
107                     if (schemaNode instanceof ListSchemaNode) {
108                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
109                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
110                         final OrderedMapNode readList = (OrderedMapNode) readData;
111                         if (readList == null || readList.getValue().isEmpty()) {
112                             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
113                                     newReadWriteTransaction);
114                             return newReadWriteTransaction.commit();
115                         } else {
116                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
117                                     path.getParent().getParent());
118                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
119                                     schemaContext, transactionNode.getTransactionChainHandler());
120                             makePost(path, readData, schemaContext, transactionNode.getTransactionChainHandler(),
121                                     newReadWriteTransaction);
122                             return newReadWriteTransaction.commit();
123                         }
124                     } else {
125                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
126                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
127
128                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
129                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
130                             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
131                                     newReadWriteTransaction);
132                             return newReadWriteTransaction.commit();
133                         } else {
134                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
135                                     path.getParent().getParent());
136                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
137                                     schemaContext, transactionNode.getTransactionChainHandler());
138                             makePost(path, readData, schemaContext, transactionNode.getTransactionChainHandler(),
139                                     newReadWriteTransaction);
140                             return newReadWriteTransaction.commit();
141                         }
142                     }
143                 case "last":
144                     makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
145                             newReadWriteTransaction);
146                     return newReadWriteTransaction.commit();
147                 case "before":
148                     if (schemaNode instanceof ListSchemaNode) {
149                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
150                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
151                         final OrderedMapNode readList = (OrderedMapNode) readData;
152                         if (readList == null || readList.getValue().isEmpty()) {
153                             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
154                                     newReadWriteTransaction);
155                             return newReadWriteTransaction.commit();
156                         } else {
157                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
158                                     data, schemaContext, point, readList, true,
159                                     transactionNode.getTransactionChainHandler());
160                             return newReadWriteTransaction.commit();
161                         }
162                     } else {
163                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
164                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
165
166                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
167                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
168                             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
169                                     newReadWriteTransaction);
170                             return newReadWriteTransaction.commit();
171                         } else {
172                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
173                                     path, data, schemaContext, point, readLeafList, true,
174                                     transactionNode.getTransactionChainHandler());
175                             return newReadWriteTransaction.commit();
176                         }
177                     }
178                 case "after":
179                     if (schemaNode instanceof ListSchemaNode) {
180                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
181                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
182                         final OrderedMapNode readList = (OrderedMapNode) readData;
183                         if (readList == null || readList.getValue().isEmpty()) {
184                             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
185                                     newReadWriteTransaction);
186                             return newReadWriteTransaction.commit();
187                         } else {
188                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
189                                     data, schemaContext, point, readList, false,
190                                     transactionNode.getTransactionChainHandler());
191                             return newReadWriteTransaction.commit();
192                         }
193                     } else {
194                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
195                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
196
197                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
198                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
199                             makePost(path, data, schemaContext, transactionNode.getTransactionChainHandler(),
200                                     newReadWriteTransaction);
201                             return newReadWriteTransaction.commit();
202                         } else {
203                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
204                                     path, data, schemaContext, point, readLeafList, true,
205                                     transactionNode.getTransactionChainHandler());
206                             return newReadWriteTransaction.commit();
207                         }
208                     }
209                 default:
210                     throw new RestconfDocumentedException(
211                             "Used bad value of insert parameter. Possible values are first, last, before or after, "
212                                     + "but was: " + insert);
213             }
214         }
215     }
216
217     private static void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
218             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
219             final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
220             final boolean before, final TransactionChainHandler transactionChainHandler) {
221         rwTransaction.delete(datastore, path.getParent().getParent());
222         final InstanceIdentifierContext<?> instanceIdentifier =
223                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
224         int lastItemPosition = 0;
225         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
226             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
227                 break;
228             }
229             lastItemPosition++;
230         }
231         if (!before) {
232             lastItemPosition++;
233         }
234         int lastInsertedPosition = 0;
235         final NormalizedNode<?, ?> emptySubtree =
236                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
237         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
238         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
239             if (lastInsertedPosition == lastItemPosition) {
240                 TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, path,
241                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
242                 rwTransaction.put(datastore, path, payload);
243             }
244             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
245             TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, childPath,
246                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
247             rwTransaction.put(datastore, childPath, nodeChild);
248             lastInsertedPosition++;
249         }
250     }
251
252     private static void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
253             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
254             final SchemaContext schemaContext, final String point, final MapNode readList, final boolean before,
255             final TransactionChainHandler transactionChainHandler) {
256         rwTransaction.delete(datastore, path.getParent().getParent());
257         final InstanceIdentifierContext<?> instanceIdentifier =
258                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
259         int lastItemPosition = 0;
260         for (final MapEntryNode mapEntryNode : readList.getValue()) {
261             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
262                 break;
263             }
264             lastItemPosition++;
265         }
266         if (!before) {
267             lastItemPosition++;
268         }
269         int lastInsertedPosition = 0;
270         final NormalizedNode<?, ?> emptySubtree =
271                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
272         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
273         for (final MapEntryNode mapEntryNode : readList.getValue()) {
274             if (lastInsertedPosition == lastItemPosition) {
275                 TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, path,
276                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
277                 rwTransaction.put(datastore, path, payload);
278             }
279             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
280             TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, childPath,
281                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
282             rwTransaction.put(datastore, childPath, mapEntryNode);
283             lastInsertedPosition++;
284         }
285     }
286
287     private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
288             final SchemaContext schemaContext, final TransactionChainHandler transactionChainHandler,
289             final DOMDataTreeReadWriteTransaction transaction) {
290         if (data instanceof MapNode) {
291             boolean merge = false;
292             for (final MapEntryNode child : ((MapNode) data).getValue()) {
293                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
294                 TransactionUtil.checkItemDoesNotExists(
295                         transactionChainHandler, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
296                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
297                 if (!merge) {
298                     merge = true;
299                     TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
300                     final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
301                     transaction.merge(LogicalDatastoreType.CONFIGURATION,
302                             YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
303                 }
304                 transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
305             }
306         } else {
307             TransactionUtil.checkItemDoesNotExists(
308                     transactionChainHandler, transaction, LogicalDatastoreType.CONFIGURATION, path,
309                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
310
311             TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
312             transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
313         }
314     }
315
316     /**
317      * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
318      *
319      * @param uriInfo
320      *             uri info
321      * @param transactionNode
322      *             wrapper for data of transaction
323      * @param schemaContextRef
324      *            reference to {@link SchemaContext}
325      * @return {@link URI}
326      */
327     private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
328             final SchemaContextRef schemaContextRef) {
329         if (uriInfo == null) {
330             return null;
331         }
332
333         final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
334         uriBuilder.path("data");
335         uriBuilder.path(ParserIdentifier
336                 .stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(),
337                 schemaContextRef.get()));
338
339         return uriBuilder.build();
340     }
341
342     private static void simplePost(final DOMDataTreeReadWriteTransaction rwTransaction,
343             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
344             final SchemaContext schemaContext, final TransactionChainHandler transactionChainHandler) {
345         TransactionUtil.checkItemDoesNotExists(transactionChainHandler, rwTransaction, datastore, path,
346                 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
347         TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
348         rwTransaction.put(datastore, path, payload);
349     }
350 }