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