--- /dev/null
+/* Copyright (c) 2019 AT&T Intellectual Property. #\r
+# #\r
+# Licensed under the Apache License, Version 2.0 (the "License"); #\r
+# you may not use this file except in compliance with the License. #\r
+# You may obtain a copy of the License at #\r
+# #\r
+# http://www.apache.org/licenses/LICENSE-2.0 #\r
+# #\r
+# Unless required by applicable law or agreed to in writing, software #\r
+# distributed under the License is distributed on an "AS IS" BASIS, #\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #\r
+# See the License for the specific language governing permissions and #\r
+# limitations under the License. #\r
+##############################################################################*/\r
+\r
+\r
+package org.oran.otf.camunda.delegate.otf.common.runnable;\r
+\r
+\r
+import org.oran.otf.camunda.exception.TestExecutionException;\r
+import org.oran.otf.common.model.TestExecution;\r
+import org.oran.otf.common.model.TestHead;\r
+import org.oran.otf.common.model.local.TestHeadRequest;\r
+import org.oran.otf.common.model.local.TestHeadResult;\r
+import org.oran.otf.common.utility.Utility;\r
+import org.oran.otf.common.utility.gson.Convert;\r
+import org.oran.otf.common.utility.http.RequestUtility;\r
+import com.google.common.base.Strings;\r
+import com.google.gson.JsonParser;\r
+import com.mongodb.client.result.UpdateResult;\r
+import java.io.IOException;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.concurrent.Callable;\r
+\r
+import org.apache.http.HttpHeaders;\r
+import org.apache.http.HttpResponse;\r
+import org.apache.http.util.EntityUtils;\r
+import org.oran.otf.common.utility.http.HeadersUtility;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+import org.springframework.data.mongodb.core.MongoTemplate;\r
+import org.springframework.data.mongodb.core.query.Criteria;\r
+import org.springframework.data.mongodb.core.query.Query;\r
+import org.springframework.data.mongodb.core.query.Update;\r
+\r
+// TODO : Create a constructor that does not take a testexecution object as a parameter. This means\r
+// that the result should only be returned, and the call to saveResult should be avoided.\r
+public class TestHeadCallable implements Callable<TestHeadResult> {\r
+\r
+ private static Logger logger = LoggerFactory.getLogger(TestHeadCallable.class);\r
+ private final String logPrefix = Utility.getLoggerPrefix();\r
+ private final TestExecution testExecution;\r
+\r
+ private final int timeoutInMillis;\r
+ private final String httpMethod;\r
+ private final Map<String, String> headers;\r
+ private final Map<String, Object> body;\r
+ private final TestHead testHead;\r
+ private final String activityId;\r
+\r
+ private final MongoTemplate mongoOperation;\r
+\r
+ private String url;\r
+ private TestHeadResult result;\r
+ private Date startTime;\r
+ private Date endTime;\r
+\r
+ public TestHeadCallable(\r
+ int timeoutInMillis,\r
+ String httpMethod,\r
+ Map<String, String> headers,\r
+ Map<String, Object> vthInput,\r
+ TestHead testHead,\r
+ String activityId,\r
+ TestExecution testExecution,\r
+ MongoTemplate mongoOperation) {\r
+ this.timeoutInMillis = timeoutInMillis;\r
+ this.httpMethod = httpMethod;\r
+ this.headers = headers;\r
+ this.body = vthInput;\r
+ this.testHead = testHead;\r
+ this.activityId = activityId;\r
+ this.testExecution = testExecution;\r
+\r
+ this.mongoOperation = mongoOperation;\r
+\r
+ // Generate the url after the test head is set.\r
+ this.url = generateUrl();\r
+ }\r
+\r
+ @Override\r
+ public TestHeadResult call() throws Exception {\r
+ // If simulation mode is set, then send back expected result after expected delay\r
+ if (testExecution.getHistoricTestInstance().isSimulationMode()) {\r
+ logger.info(logPrefix + "Start call to test head in simulation mode.");\r
+ startTime = new Date(System.currentTimeMillis());\r
+ Map<String, Object> response =\r
+ simulateVTH(\r
+ this.activityId, testExecution.getHistoricTestInstance().getSimulationVthInput());\r
+ endTime = new Date(System.currentTimeMillis());\r
+ logger.info(logPrefix + "Finished call to test head in simulation mode.");\r
+\r
+ //TODO: This will need to change if enhancement is made to allow status codes\r
+ TestHeadResult result = generateResult(response);\r
+ testExecution.getTestHeadResults().add(result);\r
+ saveResult(testExecution);\r
+ return result;\r
+ }\r
+ logger.info(logPrefix + "Start call to test head.");\r
+ HttpResponse response = null;\r
+ TestHeadResult result = null;\r
+ // Set the start time right before the request.\r
+ startTime = new Date(System.currentTimeMillis());\r
+\r
+ // add api key to headers if required\r
+ setApiKeyIfEnabled();\r
+\r
+ //TODO RG Added to slow Execution\r
+ //Thread.sleep(60000);\r
+\r
+ try {\r
+ switch (httpMethod.toLowerCase()) {\r
+ case "post":\r
+ response =\r
+ timeoutInMillis > 0\r
+ ? RequestUtility.postSync(\r
+ url, Convert.mapToJson(body), headers, timeoutInMillis, false)\r
+ : RequestUtility.postSync(url, Convert.mapToJson(body), headers, false);\r
+ break;\r
+ case "get":\r
+ response =\r
+ timeoutInMillis > 0\r
+ ? RequestUtility.getSync(url, headers, timeoutInMillis, false)\r
+ : RequestUtility.getSync(url, headers, false);\r
+ break;\r
+ default:\r
+ throw new RuntimeException();\r
+ }\r
+ // Set the end time when the request returns.\r
+ endTime = new Date(System.currentTimeMillis());\r
+ logger.info(logPrefix + "Finished call to test head.");\r
+\r
+ // Generate and return the result.\r
+ result = generateResult(response);\r
+ } catch (Exception e) {\r
+ Map<String, Object> error = new HashMap<>();\r
+ error.put("error", e.getMessage());\r
+ result = generateFailedResult(error);\r
+\r
+ logger.info(logPrefix + "There was an error calling the test head.");\r
+ }\r
+\r
+ testExecution.getTestHeadResults().add(result);\r
+ saveResult(testExecution);\r
+ return result;\r
+ }\r
+\r
+ private void setApiKeyIfEnabled(){\r
+ if(this.testHead.getAuthorizationEnabled() != null && this.testHead.getAuthorizationEnabled().booleanValue()){\r
+ this.headers.put(HttpHeaders.AUTHORIZATION, testHead.getAuthorizationType() + " " + testHead.getAuthorizationCredential());\r
+ }\r
+ }\r
+\r
+ private String generateUrl() {\r
+ String resource = testHead.getResourcePath();\r
+ // Prepend a forward-slash if the resource path exists, and does NOT already start with one. The\r
+ // order of this condition is relevant for null-checks.\r
+ if (!Strings.isNullOrEmpty(resource) && !resource.startsWith("/")) {\r
+ resource = "/" + resource;\r
+ }\r
+ return testHead.getHostname() + ":" + testHead.getPort() + resource;\r
+ }\r
+\r
+ private TestHeadResult generateFailedResult(Map<String, Object> error) {\r
+ int statusCodeError = -1;\r
+ TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);\r
+\r
+ return new TestHeadResult(\r
+ testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, statusCodeError, requestContent, error, startTime, endTime);\r
+ }\r
+\r
+ private TestHeadResult generateResult(HttpResponse response) throws IOException {\r
+ String sRes = EntityUtils.toString(response.getEntity());\r
+ JsonParser parser = new JsonParser();\r
+ Map<String, Object> res;\r
+ try {\r
+ res = Convert.jsonToMap(sRes);\r
+ } catch (Exception e) {\r
+ res = new HashMap<>();\r
+ res.put("response", sRes);\r
+ }\r
+\r
+ TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);\r
+\r
+ return new TestHeadResult(\r
+ testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, response.getStatusLine().getStatusCode(), requestContent, res, startTime, endTime);\r
+ }\r
+\r
+ private TestHeadResult generateResult(Map<String, Object> res) {\r
+\r
+ //TODO: This will need to change if enhancement is made to allow status codes\r
+ TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);\r
+\r
+ return new TestHeadResult(\r
+ testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, 200, requestContent, res, startTime, endTime);\r
+ }\r
+\r
+ private void saveResult(TestExecution testExecution) {\r
+ Query query = new Query();\r
+ query.addCriteria(Criteria.where("_id").is(testExecution.get_id()));\r
+ // Also add businessKey as a criteria because the object won't be found if the business key\r
+ // was somehow modified in the workflow.\r
+ query.addCriteria(Criteria.where("businessKey").is(testExecution.getBusinessKey()));\r
+ Update update = new Update();\r
+ update.set("testHeadResults", testExecution.getTestHeadResults());\r
+ UpdateResult result = mongoOperation.updateFirst(query, update, TestExecution.class);\r
+ // Check the status of the findAndUpdate database, and appropriately handle the errors.\r
+ if (result.getMatchedCount() == 0) {\r
+ throw new TestExecutionException(\r
+ String.format(\r
+ "Unable to log the test result because a testExecution associated with _id, %s and businessKey %s, was not found.",\r
+ testExecution.get_id(), testExecution.getBusinessKey()));\r
+ } else if (result.getModifiedCount() == 0) {\r
+ throw new TestExecutionException("Unable to persist the testExecution to the database.");\r
+ }\r
+ }\r
+\r
+ private Map<String, Object> simulateVTH(String activityId, Map<String, Object> simulationVth) {\r
+ int delay = 0;\r
+ Map response = new HashMap<String, Object>();\r
+ if (simulationVth.containsKey(activityId)) {\r
+ Object obj = simulationVth.get(activityId);\r
+ if (obj instanceof Map) {\r
+ simulationVth = (Map) obj;\r
+ }\r
+ } else {\r
+ return null;\r
+ }\r
+\r
+ if (simulationVth.containsKey("delay")) {\r
+ Object obj = simulationVth.get("delay");\r
+ if (obj instanceof Integer) {\r
+ delay = (int) obj;\r
+ }\r
+ }\r
+\r
+ if (simulationVth.containsKey("response")) {\r
+ Object obj = simulationVth.get("response");\r
+ if (obj instanceof Map) {\r
+ response = (Map) obj;\r
+ }\r
+ }\r
+ logger.info(logPrefix + "Starting simulated call to test head.");\r
+\r
+ try {\r
+ Thread.sleep(delay);\r
+ } catch (InterruptedException e) {\r
+ e.printStackTrace();\r
+ logger.info(logPrefix + "Error simulating call to test head.");\r
+ return null;\r
+ }\r
+ logger.info(logPrefix + "Finished simulating call to test head.");\r
+ return response;\r
+ }\r
+}\r