2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.mso.client.aai;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.Optional;
30 import java.util.UUID;
32 import javax.ws.rs.core.GenericType;
33 import javax.ws.rs.core.Response;
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;
47 import com.fasterxml.jackson.core.type.TypeReference;
48 import com.fasterxml.jackson.databind.ObjectMapper;
49 import com.google.common.base.Joiner;
51 public class AAITransactionalClient extends AAIClient {
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) {
59 this.version = version;
60 this.transactions = new Transactions();
64 private void startTransaction() {
65 Transaction transaction = new Transaction();
66 transactions.getTransactions().add(transaction);
67 currentTransaction = transaction;
71 * adds an additional transaction and closes the previous transaction
73 * @return AAITransactionalClient
75 public AAITransactionalClient beginNewTransaction() {
81 * creates a new object in A&AI
83 * @param obj - can be any object which will marshal into a valid A&AI payload
87 public AAITransactionalClient create(AAIResourceUri uri, Object obj) {
88 currentTransaction.getPut().add(new OperationBody().withUri(uri.build().toString()).withBody(obj));
89 incrementActionAmount();
94 * creates a new object in A&AI with no payload body
99 public AAITransactionalClient createEmpty(AAIResourceUri uri) {
100 currentTransaction.getPut().add(new OperationBody().withUri(uri.build().toString()).withBody(new HashMap<String, String>()));
101 incrementActionAmount();
106 * Adds a relationship between two objects in A&AI
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();
119 * relationship between multiple objects in A&AI - connects A to all objects specified in list
125 public AAITransactionalClient connect(AAIResourceUri uriA, List<AAIResourceUri> uris) {
126 for (AAIResourceUri uri : uris) {
127 this.connect(uriA, uri);
133 * Removes relationship from two objects in A&AI
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();
147 * Removes relationship from multiple objects - disconnects A from all objects specified in list
152 public AAITransactionalClient disconnect(AAIResourceUri uriA, List<AAIResourceUri> uris) {
153 for (AAIResourceUri uri : uris) {
154 this.disconnect(uriA, uri);
159 * Deletes object from A&AI. Automatically handles resource-version.
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();
175 * @param obj - can be any object which will marshal into a valid A&AI payload
179 public AAITransactionalClient update(AAIResourceUri uri, Object obj) {
180 currentTransaction.getPatch().add(new OperationBody().withUri(uri.build().toString()).withBody(obj));
181 incrementActionAmount();
185 private void incrementActionAmount() {
189 * Executes all created transactions in A&AI
190 * @throws BulkProcessFailed
192 public void execute() throws BulkProcessFailed {
193 RestClient client = this.createClient(AAIUriFactory.createResourceUri(AAIObjectType.BULK_PROCESS));
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());
202 throw new BulkProcessFailed("Transactions acccepted by A&AI, but there was no response. Unsure of result.");
205 this.transactions.getTransactions().clear();
206 this.currentTransaction = null;
207 this.actionCount = 0;
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<>();
218 for (String result : results) {
219 parsed.add(mapper.readValue(result, new TypeReference<Map<String, Object>>(){}));
221 } catch (IOException e) {
222 metricsLogger.error("could not map json", e);
224 for (Map<String, Object> map : parsed) {
225 for (Entry<String, Object> entry : map.entrySet()) {
226 if (!entry.getKey().matches("2\\d\\d")) {
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();
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);
243 if (!errorMessages.isEmpty()) {
244 return Optional.of(Joiner.on("\n").join(errorMessages));
246 return Optional.empty();
249 private Relationship buildRelationship(AAIUri uri) {
250 final Relationship result = new Relationship();
251 result.setRelatedLink(uri.build().toString());
256 protected AAIVersion getVersion() {
260 protected Transactions getTransactions() {
261 return this.transactions;