Update MRI projects for Aluminium
[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.common.errors.RestconfError;
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.EffectiveModelContext;
38 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
40
41 /**
42  * Util class to post data to DS.
43  *
44  */
45 public final class PostDataTransactionUtil {
46     private PostDataTransactionUtil() {
47         throw new UnsupportedOperationException("Util class.");
48     }
49
50     /**
51      * Check mount point and prepare variables for post data. Close {@link DOMTransactionChain} inside of object
52      * {@link TransactionVarsWrapper} provided as a parameter.
53      *
54      * @param uriInfo
55      *
56      * @param payload
57      *             data
58      * @param transactionNode
59      *             wrapper for transaction data
60      * @param schemaContextRef
61      *             reference to actual {@link SchemaContext}
62      * @param point
63      *             point
64      * @param insert
65      *             insert
66      * @return {@link Response}
67      */
68     public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
69             final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert,
70             final String point) {
71         final FluentFuture<? extends CommitInfo> future = submitData(
72                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
73                 transactionNode, schemaContextRef.get(), insert, point);
74         final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
75         final ResponseFactory dataFactory = new ResponseFactory(Status.CREATED).location(location);
76         //This method will close transactionChain
77         FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory,
78                 transactionNode.getTransactionChain());
79         return dataFactory.build();
80     }
81
82     /**
83      * Post data by type.
84      *
85      * @param path
86      *             path
87      * @param data
88      *             data
89      * @param transactionNode
90      *             wrapper for data to transaction
91      * @param schemaContext
92      *             schema context of data
93      * @param point
94      *             query parameter
95      * @param insert
96      *             query parameter
97      * @return {@link FluentFuture}
98      */
99     private static FluentFuture<? extends CommitInfo> submitData(final YangInstanceIdentifier path,
100             final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
101             final EffectiveModelContext schemaContext, final String insert, final String point) {
102         final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
103         final DOMDataTreeReadWriteTransaction newReadWriteTransaction = transactionChain.newReadWriteTransaction();
104         if (insert == null) {
105             makePost(path, data, schemaContext, transactionChain, newReadWriteTransaction);
106             return newReadWriteTransaction.commit();
107         } else {
108             final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
109             switch (insert) {
110                 case "first":
111                     if (schemaNode instanceof ListSchemaNode) {
112                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
113                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
114                         final OrderedMapNode readList = (OrderedMapNode) readData;
115                         if (readList == null || readList.getValue().isEmpty()) {
116                             makePost(path, data, schemaContext, transactionChain,
117                                     newReadWriteTransaction);
118                             return newReadWriteTransaction.commit();
119                         } else {
120                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
121                                     path.getParent().getParent());
122                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
123                                     schemaContext, transactionChain);
124                             makePost(path, readData, schemaContext, transactionChain,
125                                     newReadWriteTransaction);
126                             return newReadWriteTransaction.commit();
127                         }
128                     } else {
129                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
130                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
131
132                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
133                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
134                             makePost(path, data, schemaContext, transactionChain,
135                                     newReadWriteTransaction);
136                             return newReadWriteTransaction.commit();
137                         } else {
138                             newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
139                                     path.getParent().getParent());
140                             simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
141                                     schemaContext, transactionChain);
142                             makePost(path, readData, schemaContext, transactionChain,
143                                     newReadWriteTransaction);
144                             return newReadWriteTransaction.commit();
145                         }
146                     }
147                 case "last":
148                     makePost(path, data, schemaContext, transactionChain,
149                             newReadWriteTransaction);
150                     return newReadWriteTransaction.commit();
151                 case "before":
152                     if (schemaNode instanceof ListSchemaNode) {
153                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
154                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
155                         final OrderedMapNode readList = (OrderedMapNode) readData;
156                         if (readList == null || readList.getValue().isEmpty()) {
157                             makePost(path, data, schemaContext, transactionChain,
158                                     newReadWriteTransaction);
159                             return newReadWriteTransaction.commit();
160                         } else {
161                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
162                                     data, schemaContext, point, readList, true,
163                                     transactionChain);
164                             return newReadWriteTransaction.commit();
165                         }
166                     } else {
167                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
168                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
169
170                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
171                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
172                             makePost(path, data, schemaContext, transactionChain,
173                                     newReadWriteTransaction);
174                             return newReadWriteTransaction.commit();
175                         } else {
176                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
177                                     path, data, schemaContext, point, readLeafList, true,
178                                     transactionChain);
179                             return newReadWriteTransaction.commit();
180                         }
181                     }
182                 case "after":
183                     if (schemaNode instanceof ListSchemaNode) {
184                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
185                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
186                         final OrderedMapNode readList = (OrderedMapNode) readData;
187                         if (readList == null || readList.getValue().isEmpty()) {
188                             makePost(path, data, schemaContext, transactionChain,
189                                     newReadWriteTransaction);
190                             return newReadWriteTransaction.commit();
191                         } else {
192                             insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
193                                     data, schemaContext, point, readList, false,
194                                     transactionChain);
195                             return newReadWriteTransaction.commit();
196                         }
197                     } else {
198                         final NormalizedNode<?, ?> readData = PutDataTransactionUtil.readList(path.getParent(),
199                             schemaContext, transactionNode.getTransactionChainHandler(), schemaNode);
200
201                         final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
202                         if (readLeafList == null || readLeafList.getValue().isEmpty()) {
203                             makePost(path, data, schemaContext, transactionChain,
204                                     newReadWriteTransaction);
205                             return newReadWriteTransaction.commit();
206                         } else {
207                             insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
208                                     path, data, schemaContext, point, readLeafList, true,
209                                     transactionChain);
210                             return newReadWriteTransaction.commit();
211                         }
212                     }
213                 default:
214                     throw new RestconfDocumentedException(
215                             "Used bad value of insert parameter. Possible values are first, last, before or after, "
216                                     + "but was: " + insert, RestconfError.ErrorType.PROTOCOL,
217                             RestconfError.ErrorTag.BAD_ATTRIBUTE);
218             }
219         }
220     }
221
222     private static void insertWithPointLeafListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
223             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
224             final EffectiveModelContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
225             final boolean before, final DOMTransactionChain transactionChain) {
226         rwTransaction.delete(datastore, path.getParent().getParent());
227         final InstanceIdentifierContext<?> instanceIdentifier =
228                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
229         int lastItemPosition = 0;
230         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
231             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
232                 break;
233             }
234             lastItemPosition++;
235         }
236         if (!before) {
237             lastItemPosition++;
238         }
239         int lastInsertedPosition = 0;
240         final NormalizedNode<?, ?> emptySubtree =
241                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
242         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
243         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
244             if (lastInsertedPosition == lastItemPosition) {
245                 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
246                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
247                 rwTransaction.put(datastore, path, payload);
248             }
249             final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
250             TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
251                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
252             rwTransaction.put(datastore, childPath, nodeChild);
253             lastInsertedPosition++;
254         }
255     }
256
257     private static void insertWithPointListPost(final DOMDataTreeReadWriteTransaction rwTransaction,
258             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
259             final EffectiveModelContext schemaContext, final String point, final MapNode readList, final boolean before,
260             final DOMTransactionChain transactionChain) {
261         rwTransaction.delete(datastore, path.getParent().getParent());
262         final InstanceIdentifierContext<?> instanceIdentifier =
263                 ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.empty());
264         int lastItemPosition = 0;
265         for (final MapEntryNode mapEntryNode : readList.getValue()) {
266             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
267                 break;
268             }
269             lastItemPosition++;
270         }
271         if (!before) {
272             lastItemPosition++;
273         }
274         int lastInsertedPosition = 0;
275         final NormalizedNode<?, ?> emptySubtree =
276                 ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
277         rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
278         for (final MapEntryNode mapEntryNode : readList.getValue()) {
279             if (lastInsertedPosition == lastItemPosition) {
280                 TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
281                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
282                 rwTransaction.put(datastore, path, payload);
283             }
284             final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
285             TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, childPath,
286                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
287             rwTransaction.put(datastore, childPath, mapEntryNode);
288             lastInsertedPosition++;
289         }
290     }
291
292     private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
293             final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
294             final DOMDataTreeReadWriteTransaction transaction) {
295         if (data instanceof MapNode) {
296             boolean merge = false;
297             for (final MapEntryNode child : ((MapNode) data).getValue()) {
298                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
299                 TransactionUtil.checkItemDoesNotExists(
300                         transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
301                         RestconfDataServiceConstant.PostData.POST_TX_TYPE);
302                 if (!merge) {
303                     merge = true;
304                     TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
305                     final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
306                     transaction.merge(LogicalDatastoreType.CONFIGURATION,
307                             YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
308                 }
309                 transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
310             }
311         } else {
312             TransactionUtil.checkItemDoesNotExists(
313                     transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
314                     RestconfDataServiceConstant.PostData.POST_TX_TYPE);
315
316             TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
317             transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
318         }
319     }
320
321     /**
322      * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
323      *
324      * @param uriInfo
325      *             uri info
326      * @param transactionNode
327      *             wrapper for data of transaction
328      * @param schemaContextRef
329      *            reference to {@link SchemaContext}
330      * @return {@link URI}
331      */
332     private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
333             final SchemaContextRef schemaContextRef) {
334         if (uriInfo == null) {
335             return null;
336         }
337
338         final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
339         uriBuilder.path("data");
340         uriBuilder.path(ParserIdentifier
341                 .stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(),
342                 schemaContextRef.get()));
343
344         return uriBuilder.build();
345     }
346
347     private static void simplePost(final DOMDataTreeReadWriteTransaction rwTransaction,
348             final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
349             final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
350         TransactionUtil.checkItemDoesNotExists(transactionChain, rwTransaction, datastore, path,
351                 RestconfDataServiceConstant.PostData.POST_TX_TYPE);
352         TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
353         rwTransaction.put(datastore, path, payload);
354     }
355 }