Remove RestconfError.ErrorTag
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / PatchDataTransactionUtil.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.collect.ImmutableList;
11 import com.google.common.collect.Lists;
12 import com.google.common.util.concurrent.FluentFuture;
13 import java.util.ArrayList;
14 import java.util.List;
15 import javax.ws.rs.core.Response.Status;
16 import org.opendaylight.mdsal.common.api.CommitInfo;
17 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
18 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
19 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
20 import org.opendaylight.restconf.common.errors.RestconfError;
21 import org.opendaylight.restconf.common.patch.PatchContext;
22 import org.opendaylight.restconf.common.patch.PatchEntity;
23 import org.opendaylight.restconf.common.patch.PatchStatusContext;
24 import org.opendaylight.restconf.common.patch.PatchStatusEntity;
25 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfStrategy;
26 import org.opendaylight.restconf.nb.rfc8040.rests.transactions.RestconfTransaction;
27 import org.opendaylight.yangtools.yang.common.ErrorTag;
28 import org.opendaylight.yangtools.yang.common.ErrorType;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 public final class PatchDataTransactionUtil {
36     private static final Logger LOG = LoggerFactory.getLogger(PatchDataTransactionUtil.class);
37     // FIXME: why is this used from other contexts?
38     static final String PATCH_TX_TYPE = "Patch";
39
40     private PatchDataTransactionUtil() {
41         // Hidden on purpose
42     }
43
44     /**
45      * Process edit operations of one {@link PatchContext}. Close {@link DOMTransactionChain} if any inside of object
46      * {@link RestconfStrategy} provided as a parameter.
47      *
48      * @param context       Patch context to be processed
49      * @param strategy      object that perform the actual DS operations
50      * @param schemaContext Global schema context
51      * @return {@link PatchStatusContext}
52      */
53     public static PatchStatusContext patchData(final PatchContext context, final RestconfStrategy strategy,
54                                                final EffectiveModelContext schemaContext) {
55         final List<PatchStatusEntity> editCollection = new ArrayList<>();
56         boolean noError = true;
57         final RestconfTransaction transaction = strategy.prepareWriteExecution();
58
59         for (final PatchEntity patchEntity : context.getData()) {
60             if (noError) {
61                 switch (patchEntity.getOperation()) {
62                     case CREATE:
63                         try {
64                             createDataWithinTransaction(patchEntity.getTargetNode(), patchEntity.getNode(),
65                                 schemaContext, transaction);
66                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
67                         } catch (final RestconfDocumentedException e) {
68                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
69                                     false, Lists.newArrayList(e.getErrors())));
70                             noError = false;
71                         }
72                         break;
73                     case DELETE:
74                         try {
75                             deleteDataWithinTransaction(patchEntity.getTargetNode(), transaction);
76                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
77                         } catch (final RestconfDocumentedException e) {
78                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
79                                     false, Lists.newArrayList(e.getErrors())));
80                             noError = false;
81                         }
82                         break;
83                     case MERGE:
84                         try {
85                             mergeDataWithinTransaction(patchEntity.getTargetNode(), patchEntity.getNode(),
86                                 schemaContext, transaction);
87                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
88                         } catch (final RestconfDocumentedException e) {
89                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
90                                     false, Lists.newArrayList(e.getErrors())));
91                             noError = false;
92                         }
93                         break;
94                     case REPLACE:
95                         try {
96                             replaceDataWithinTransaction(patchEntity.getTargetNode(), patchEntity.getNode(),
97                                 schemaContext, transaction);
98                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
99                         } catch (final RestconfDocumentedException e) {
100                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
101                                     false, Lists.newArrayList(e.getErrors())));
102                             noError = false;
103                         }
104                         break;
105                     case REMOVE:
106                         try {
107                             removeDataWithinTransaction(patchEntity.getTargetNode(), transaction);
108                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
109                         } catch (final RestconfDocumentedException e) {
110                             editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
111                                     false, Lists.newArrayList(e.getErrors())));
112                             noError = false;
113                         }
114                         break;
115                     default:
116                         editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
117                                 false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
118                                 ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang Patch operation"))));
119                         noError = false;
120                         break;
121                 }
122             } else {
123                 break;
124             }
125         }
126
127         // if no errors then submit transaction, otherwise cancel
128         if (noError) {
129             final ResponseFactory response = new ResponseFactory(Status.OK);
130             final FluentFuture<? extends CommitInfo> future = transaction.commit();
131
132             try {
133                 //This method will close transactionChain if any
134                 FutureCallbackTx.addCallback(future, PATCH_TX_TYPE, response, strategy, null);
135             } catch (final RestconfDocumentedException e) {
136                 // if errors occurred during transaction commit then patch failed and global errors are reported
137                 return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
138                         Lists.newArrayList(e.getErrors()));
139             }
140
141             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
142         } else {
143             transaction.cancel();
144             return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
145         }
146     }
147
148     /**
149      * Create data within one transaction, return error if already exists.
150      *
151      * @param path          Path for data to be created
152      * @param payload       Data to be created
153      * @param transaction   A handle to a set of DS operations
154      */
155     private static void createDataWithinTransaction(final YangInstanceIdentifier path, final NormalizedNode payload,
156                                                     final EffectiveModelContext schemaContext,
157                                                     final RestconfTransaction transaction) {
158         LOG.trace("POST {} within Restconf Patch: {} with payload {}", LogicalDatastoreType.CONFIGURATION.name(),
159             path, payload);
160         transaction.create(path, payload, schemaContext);
161     }
162
163     /**
164      * Remove data within one transaction.
165      *
166      * @param path     Path for data to be deleted
167      * @param transaction   A handle to a set of DS operations
168      */
169     private static void deleteDataWithinTransaction(final YangInstanceIdentifier path,
170                                                     final RestconfTransaction transaction) {
171         LOG.trace("Delete {} within Restconf Patch: {}", LogicalDatastoreType.CONFIGURATION.name(), path);
172         transaction.delete(path);
173     }
174
175     /**
176      * Merge data within one transaction.
177      *
178      * @param path     Path for data to be merged
179      * @param payload  Data to be merged
180      * @param transaction   A handle to a set of DS operations
181      */
182     private static void mergeDataWithinTransaction(final YangInstanceIdentifier path, final NormalizedNode payload,
183                                                    final EffectiveModelContext schemaContext,
184                                                    final RestconfTransaction transaction) {
185         LOG.trace("Merge {} within Restconf Patch: {} with payload {}", LogicalDatastoreType.CONFIGURATION.name(),
186             path, payload);
187         TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
188         transaction.merge(path, payload);
189     }
190
191     /**
192      * Do NOT check if data exists and remove it within one transaction.
193      *
194      * @param path     Path for data to be deleted
195      * @param transaction   A handle to a set of DS operations
196      */
197     private static void removeDataWithinTransaction(final YangInstanceIdentifier path,
198                                                     final RestconfTransaction transaction) {
199         LOG.trace("Remove {} within Restconf Patch: {}", LogicalDatastoreType.CONFIGURATION.name(), path);
200         transaction.remove(path);
201     }
202
203     /**
204      * Create data within one transaction, replace if already exists.
205      *
206      * @param path          Path for data to be created
207      * @param payload       Data to be created
208      * @param transaction   A handle to a set of DS operations
209      */
210     private static void replaceDataWithinTransaction(final YangInstanceIdentifier path, final NormalizedNode payload,
211                                                      final EffectiveModelContext schemaContext,
212                                                      final RestconfTransaction transaction) {
213         LOG.trace("PUT {} within Restconf Patch: {} with payload {}",
214             LogicalDatastoreType.CONFIGURATION.name(), path, payload);
215         transaction.replace(path, payload, schemaContext);
216     }
217 }