1 /* Copyright (c) 2019 AT&T Intellectual Property. #
\r
3 # Licensed under the Apache License, Version 2.0 (the "License"); #
\r
4 # you may not use this file except in compliance with the License. #
\r
5 # You may obtain a copy of the License at #
\r
7 # http://www.apache.org/licenses/LICENSE-2.0 #
\r
9 # Unless required by applicable law or agreed to in writing, software #
\r
10 # distributed under the License is distributed on an "AS IS" BASIS, #
\r
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
\r
12 # See the License for the specific language governing permissions and #
\r
13 # limitations under the License. #
\r
14 ##############################################################################*/
\r
17 package org.oran.otf.camunda.delegate.otf.common.runnable;
\r
20 import org.oran.otf.camunda.exception.TestExecutionException;
\r
21 import org.oran.otf.common.model.TestExecution;
\r
22 import org.oran.otf.common.model.TestHead;
\r
23 import org.oran.otf.common.model.local.TestHeadRequest;
\r
24 import org.oran.otf.common.model.local.TestHeadResult;
\r
25 import org.oran.otf.common.utility.Utility;
\r
26 import org.oran.otf.common.utility.gson.Convert;
\r
27 import org.oran.otf.common.utility.http.RequestUtility;
\r
28 import com.google.common.base.Strings;
\r
29 import com.google.gson.JsonParser;
\r
30 import com.mongodb.client.result.UpdateResult;
\r
31 import java.io.IOException;
\r
32 import java.util.Date;
\r
33 import java.util.HashMap;
\r
34 import java.util.Map;
\r
35 import java.util.concurrent.Callable;
\r
37 import org.apache.http.HttpHeaders;
\r
38 import org.apache.http.HttpResponse;
\r
39 import org.apache.http.util.EntityUtils;
\r
40 import org.oran.otf.common.utility.http.HeadersUtility;
\r
41 import org.slf4j.Logger;
\r
42 import org.slf4j.LoggerFactory;
\r
43 import org.springframework.data.mongodb.core.MongoTemplate;
\r
44 import org.springframework.data.mongodb.core.query.Criteria;
\r
45 import org.springframework.data.mongodb.core.query.Query;
\r
46 import org.springframework.data.mongodb.core.query.Update;
\r
48 // TODO : Create a constructor that does not take a testexecution object as a parameter. This means
\r
49 // that the result should only be returned, and the call to saveResult should be avoided.
\r
50 public class TestHeadCallable implements Callable<TestHeadResult> {
\r
52 private static Logger logger = LoggerFactory.getLogger(TestHeadCallable.class);
\r
53 private final String logPrefix = Utility.getLoggerPrefix();
\r
54 private final TestExecution testExecution;
\r
56 private final int timeoutInMillis;
\r
57 private final String httpMethod;
\r
58 private final Map<String, String> headers;
\r
59 private final Map<String, Object> body;
\r
60 private final TestHead testHead;
\r
61 private final String activityId;
\r
63 private final MongoTemplate mongoOperation;
\r
66 private TestHeadResult result;
\r
67 private Date startTime;
\r
68 private Date endTime;
\r
70 public TestHeadCallable(
\r
71 int timeoutInMillis,
\r
73 Map<String, String> headers,
\r
74 Map<String, Object> vthInput,
\r
77 TestExecution testExecution,
\r
78 MongoTemplate mongoOperation) {
\r
79 this.timeoutInMillis = timeoutInMillis;
\r
80 this.httpMethod = httpMethod;
\r
81 this.headers = headers;
\r
82 this.body = vthInput;
\r
83 this.testHead = testHead;
\r
84 this.activityId = activityId;
\r
85 this.testExecution = testExecution;
\r
87 this.mongoOperation = mongoOperation;
\r
89 // Generate the url after the test head is set.
\r
90 this.url = generateUrl();
\r
94 public TestHeadResult call() throws Exception {
\r
95 // If simulation mode is set, then send back expected result after expected delay
\r
96 if (testExecution.getHistoricTestInstance().isSimulationMode()) {
\r
97 logger.info(logPrefix + "Start call to test head in simulation mode.");
\r
98 startTime = new Date(System.currentTimeMillis());
\r
99 Map<String, Object> response =
\r
101 this.activityId, testExecution.getHistoricTestInstance().getSimulationVthInput());
\r
102 endTime = new Date(System.currentTimeMillis());
\r
103 logger.info(logPrefix + "Finished call to test head in simulation mode.");
\r
105 //TODO: This will need to change if enhancement is made to allow status codes
\r
106 TestHeadResult result = generateResult(response);
\r
107 testExecution.getTestHeadResults().add(result);
\r
108 saveResult(testExecution);
\r
111 logger.info(logPrefix + "Start call to test head.");
\r
112 HttpResponse response = null;
\r
113 TestHeadResult result = null;
\r
114 // Set the start time right before the request.
\r
115 startTime = new Date(System.currentTimeMillis());
\r
117 // add api key to headers if required
\r
118 setApiKeyIfEnabled();
\r
120 //TODO RG Added to slow Execution
\r
121 //Thread.sleep(60000);
\r
124 switch (httpMethod.toLowerCase()) {
\r
127 timeoutInMillis > 0
\r
128 ? RequestUtility.postSync(
\r
129 url, Convert.mapToJson(body), headers, timeoutInMillis, false)
\r
130 : RequestUtility.postSync(url, Convert.mapToJson(body), headers, false);
\r
134 timeoutInMillis > 0
\r
135 ? RequestUtility.getSync(url, headers, timeoutInMillis, false)
\r
136 : RequestUtility.getSync(url, headers, false);
\r
139 throw new RuntimeException();
\r
141 // Set the end time when the request returns.
\r
142 endTime = new Date(System.currentTimeMillis());
\r
143 logger.info(logPrefix + "Finished call to test head.");
\r
145 // Generate and return the result.
\r
146 result = generateResult(response);
\r
147 } catch (Exception e) {
\r
148 Map<String, Object> error = new HashMap<>();
\r
149 error.put("error", e.getMessage());
\r
150 result = generateFailedResult(error);
\r
152 logger.info(logPrefix + "There was an error calling the test head.");
\r
155 testExecution.getTestHeadResults().add(result);
\r
156 saveResult(testExecution);
\r
160 private void setApiKeyIfEnabled(){
\r
161 if(this.testHead.getAuthorizationEnabled() != null && this.testHead.getAuthorizationEnabled().booleanValue()){
\r
162 this.headers.put(HttpHeaders.AUTHORIZATION, testHead.getAuthorizationType() + " " + testHead.getAuthorizationCredential());
\r
166 private String generateUrl() {
\r
167 String resource = testHead.getResourcePath();
\r
168 // Prepend a forward-slash if the resource path exists, and does NOT already start with one. The
\r
169 // order of this condition is relevant for null-checks.
\r
170 if (!Strings.isNullOrEmpty(resource) && !resource.startsWith("/")) {
\r
171 resource = "/" + resource;
\r
173 return testHead.getHostname() + ":" + testHead.getPort() + resource;
\r
176 private TestHeadResult generateFailedResult(Map<String, Object> error) {
\r
177 int statusCodeError = -1;
\r
178 TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);
\r
180 return new TestHeadResult(
\r
181 testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, statusCodeError, requestContent, error, startTime, endTime);
\r
184 private TestHeadResult generateResult(HttpResponse response) throws IOException {
\r
185 String sRes = EntityUtils.toString(response.getEntity());
\r
186 JsonParser parser = new JsonParser();
\r
187 Map<String, Object> res;
\r
189 res = Convert.jsonToMap(sRes);
\r
190 } catch (Exception e) {
\r
191 res = new HashMap<>();
\r
192 res.put("response", sRes);
\r
195 TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);
\r
197 return new TestHeadResult(
\r
198 testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, response.getStatusLine().getStatusCode(), requestContent, res, startTime, endTime);
\r
201 private TestHeadResult generateResult(Map<String, Object> res) {
\r
203 //TODO: This will need to change if enhancement is made to allow status codes
\r
204 TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);
\r
206 return new TestHeadResult(
\r
207 testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, 200, requestContent, res, startTime, endTime);
\r
210 private void saveResult(TestExecution testExecution) {
\r
211 Query query = new Query();
\r
212 query.addCriteria(Criteria.where("_id").is(testExecution.get_id()));
\r
213 // Also add businessKey as a criteria because the object won't be found if the business key
\r
214 // was somehow modified in the workflow.
\r
215 query.addCriteria(Criteria.where("businessKey").is(testExecution.getBusinessKey()));
\r
216 Update update = new Update();
\r
217 update.set("testHeadResults", testExecution.getTestHeadResults());
\r
218 UpdateResult result = mongoOperation.updateFirst(query, update, TestExecution.class);
\r
219 // Check the status of the findAndUpdate database, and appropriately handle the errors.
\r
220 if (result.getMatchedCount() == 0) {
\r
221 throw new TestExecutionException(
\r
223 "Unable to log the test result because a testExecution associated with _id, %s and businessKey %s, was not found.",
\r
224 testExecution.get_id(), testExecution.getBusinessKey()));
\r
225 } else if (result.getModifiedCount() == 0) {
\r
226 throw new TestExecutionException("Unable to persist the testExecution to the database.");
\r
230 private Map<String, Object> simulateVTH(String activityId, Map<String, Object> simulationVth) {
\r
232 Map response = new HashMap<String, Object>();
\r
233 if (simulationVth.containsKey(activityId)) {
\r
234 Object obj = simulationVth.get(activityId);
\r
235 if (obj instanceof Map) {
\r
236 simulationVth = (Map) obj;
\r
242 if (simulationVth.containsKey("delay")) {
\r
243 Object obj = simulationVth.get("delay");
\r
244 if (obj instanceof Integer) {
\r
249 if (simulationVth.containsKey("response")) {
\r
250 Object obj = simulationVth.get("response");
\r
251 if (obj instanceof Map) {
\r
252 response = (Map) obj;
\r
255 logger.info(logPrefix + "Starting simulated call to test head.");
\r
258 Thread.sleep(delay);
\r
259 } catch (InterruptedException e) {
\r
260 e.printStackTrace();
\r
261 logger.info(logPrefix + "Error simulating call to test head.");
\r
264 logger.info(logPrefix + "Finished simulating call to test head.");
\r