Merge "Reorder modifiers"
[so.git] / common / src / main / java / org / openecomp / mso / client / aai / AAITransactionalClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.mso.client.aai;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Optional;
30 import java.util.UUID;
31
32 import javax.ws.rs.core.GenericType;
33 import javax.ws.rs.core.Response;
34
35 import org.onap.aai.domain.yang.Relationship;
36 import org.openecomp.mso.client.aai.entities.AAIError;
37 import org.openecomp.mso.client.aai.entities.bulkprocess.OperationBody;
38 import org.openecomp.mso.client.aai.entities.bulkprocess.Transaction;
39 import org.openecomp.mso.client.aai.entities.bulkprocess.Transactions;
40 import org.openecomp.mso.client.aai.entities.uri.AAIResourceUri;
41 import org.openecomp.mso.client.aai.entities.uri.AAIUri;
42 import org.openecomp.mso.client.aai.entities.uri.AAIUriFactory;
43 import org.openecomp.mso.client.aai.exceptions.BulkProcessFailed;
44 import org.openecomp.mso.client.policy.RestClient;
45 import org.openecomp.mso.jsonpath.JsonPathUtil;
46
47 import com.fasterxml.jackson.core.type.TypeReference;
48 import com.fasterxml.jackson.databind.ObjectMapper;
49 import com.google.common.base.Joiner;
50
51 public class AAITransactionalClient extends AAIClient {
52
53         private final Transactions transactions;
54         private Transaction currentTransaction;
55         private final AAIVersion version;
56         private int actionCount = 0;
57         protected AAITransactionalClient(AAIVersion version, UUID requestId) {
58                 super(requestId);
59                 this.version = version;
60                 this.transactions = new Transactions();
61                 startTransaction();
62         }
63         
64         private void startTransaction() {
65                 Transaction transaction = new Transaction();
66                 transactions.getTransactions().add(transaction);
67                 currentTransaction = transaction;
68         }
69         
70         /**
71          * adds an additional transaction and closes the previous transaction
72          * 
73          * @return AAITransactionalClient
74          */
75         public AAITransactionalClient beginNewTransaction() {
76                 startTransaction();
77                 return this;
78         }
79         
80         /**
81          * creates a new object in A&AI
82          * 
83          * @param obj - can be any object which will marshal into a valid A&AI payload
84          * @param uri
85          * @return
86          */
87         public AAITransactionalClient create(AAIResourceUri uri, Object obj) {
88                 currentTransaction.getPut().add(new OperationBody().withUri(uri.build().toString()).withBody(obj));
89                 incrementActionAmount();
90                 return this;
91         }
92         
93         /**
94          * creates a new object in A&AI with no payload body
95          * 
96          * @param uri
97          * @return
98          */
99         public AAITransactionalClient createEmpty(AAIResourceUri uri) {
100                 currentTransaction.getPut().add(new OperationBody().withUri(uri.build().toString()).withBody(new HashMap<String, String>()));
101                 incrementActionAmount();
102                 return this;
103         }
104         
105         /**
106          * Adds a relationship between two objects in A&AI 
107          * @param uriA
108          * @param uriB
109          * @return
110          */
111         public AAITransactionalClient connect(AAIResourceUri uriA, AAIResourceUri uriB) {
112                 AAIResourceUri uriAClone = uriA.clone();
113                 currentTransaction.getPut().add(new OperationBody().withUri(uriAClone.relationshipAPI().build().toString()).withBody(this.buildRelationship(uriB)));
114                 incrementActionAmount();
115                 return this;
116         }
117         
118         /**
119          * relationship between multiple objects in A&AI - connects A to all objects specified in list
120          * 
121          * @param uriA
122          * @param uris
123          * @return
124          */
125         public AAITransactionalClient connect(AAIResourceUri uriA, List<AAIResourceUri> uris) {
126                 for (AAIResourceUri uri : uris) {
127                         this.connect(uriA, uri);
128                 }
129                 return this;
130         }
131         
132         /**
133          * Removes relationship from two objects in A&AI
134          * 
135          * @param uriA
136          * @param uriB
137          * @return
138          */
139         public AAITransactionalClient disconnect(AAIResourceUri uriA, AAIResourceUri uriB) {
140                 AAIResourceUri uriAClone = uriA.clone();
141                 currentTransaction.getDelete().add(new OperationBody().withUri(uriAClone.relationshipAPI().build().toString()).withBody(this.buildRelationship(uriB)));
142                 incrementActionAmount();
143                 return this;
144         }
145         
146         /**
147          * Removes relationship from multiple objects - disconnects A from all objects specified in list
148          * @param uriA
149          * @param uris
150          * @return
151          */
152         public AAITransactionalClient disconnect(AAIResourceUri uriA, List<AAIResourceUri> uris) {
153                 for (AAIResourceUri uri : uris) {
154                         this.disconnect(uriA, uri);
155                 }
156                 return this;
157         }
158         /**
159          * Deletes object from A&AI. Automatically handles resource-version.
160          * 
161          * @param uri
162          * @return
163          */
164         public AAITransactionalClient delete(AAIResourceUri uri) {
165                 AAIResourcesClient client = new AAIResourcesClient();
166                 AAIResourceUri clone = uri.clone();
167                 Map<String, Object> result = client.get(new GenericType<Map<String, Object>>(){}, clone);
168                 String resourceVersion = (String) result.get("resource-version");
169                 currentTransaction.getDelete().add(new OperationBody().withUri(clone.resourceVersion(resourceVersion).build().toString()).withBody(""));
170                 incrementActionAmount();
171                 return this;
172         }
173         
174         /**
175          * @param obj - can be any object which will marshal into a valid A&AI payload
176          * @param uri
177          * @return
178          */
179         public AAITransactionalClient update(AAIResourceUri uri, Object obj) {
180                 currentTransaction.getPatch().add(new OperationBody().withUri(uri.build().toString()).withBody(obj));
181                 incrementActionAmount();
182                 return this;
183         }
184         
185         private void incrementActionAmount() {
186                 actionCount++;
187         }
188         /**
189          * Executes all created transactions in A&AI
190          * @throws BulkProcessFailed 
191          */
192         public void execute() throws BulkProcessFailed {
193                 RestClient client = this.createClient(AAIUriFactory.createResourceUri(AAIObjectType.BULK_PROCESS));
194                 try {
195                         Response response = client.put(this.transactions);
196                         if (response.hasEntity()) {
197                                 final Optional<String> errorMessage = this.locateErrorMessages(response.readEntity(String.class));
198                                 if (errorMessage.isPresent()) {
199                                         throw new BulkProcessFailed("One or more transactions failed in A&AI. Request-id=" + this.getRequestId() + ". Check logs for payloads.\nMessages:\n" + errorMessage.get());
200                                 }
201                         } else {
202                                 throw new BulkProcessFailed("Transactions acccepted by A&AI, but there was no response. Unsure of result.");
203                         }
204                 } finally {
205                         this.transactions.getTransactions().clear();
206                         this.currentTransaction = null;
207                         this.actionCount = 0;
208                 }
209         }
210         
211         protected Optional<String> locateErrorMessages(String response) {
212                 final List<String> errorMessages = new ArrayList<>();
213                 final List<String> results = JsonPathUtil.getInstance().locateResultList(response, "$..body");
214                 final ObjectMapper mapper = new ObjectMapper();
215                 if (!results.isEmpty()) {
216                         List<Map<String, Object>> parsed = new ArrayList<>();
217                         try {
218                                 for (String result : results) {
219                                         parsed.add(mapper.readValue(result, new TypeReference<Map<String, Object>>(){}));
220                                 }
221                         } catch (IOException e) {
222                                 metricsLogger.error("could not map json", e);
223                         }
224                         for (Map<String, Object> map : parsed) {
225                                 for (Entry<String, Object> entry : map.entrySet()) {
226                                         if (!entry.getKey().matches("2\\d\\d")) {
227                                                 AAIError error;
228                                                 try {
229                                                         error = mapper.readValue(entry.getValue().toString(), AAIError.class);
230                                                 } catch (IOException e) {
231                                                         metricsLogger.error("could not parse error object from A&AI", e);
232                                                         error = new AAIError();
233                                                 }
234                                                 AAIErrorFormatter formatter = new AAIErrorFormatter(error);
235                                                 String outputMessage = formatter.getMessage();
236                                                 metricsLogger.error("part of a bulk action failed in A&AI: " + entry.getValue());
237                                                 errorMessages.add(outputMessage);
238                                         }
239                                 }
240                         }
241                 }
242                 
243                 if (!errorMessages.isEmpty()) {
244                         return Optional.of(Joiner.on("\n").join(errorMessages));
245                 } else {
246                         return Optional.empty();
247                 }
248         }
249         private Relationship buildRelationship(AAIUri uri) {
250                 final Relationship result = new Relationship();
251                 result.setRelatedLink(uri.build().toString());
252                 return result;
253         }
254
255         @Override
256         protected AAIVersion getVersion() {
257                 return this.version;
258         }
259         
260         protected Transactions getTransactions() {
261                 return this.transactions;
262         }
263 }