added svcapi ui and camunda code
[it/otf.git] / otf-camunda / src / main / java / org / oran / otf / camunda / delegate / otf / common / runnable / TestHeadCallable.java
1 /*  Copyright (c) 2019 AT&T Intellectual Property.                             #\r
2 #                                                                              #\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
6 #                                                                              #\r
7 #       http://www.apache.org/licenses/LICENSE-2.0                             #\r
8 #                                                                              #\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
15 \r
16 \r
17 package org.oran.otf.camunda.delegate.otf.common.runnable;\r
18 \r
19 \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
36 \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
47 \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
51 \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
55 \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
62 \r
63   private final MongoTemplate mongoOperation;\r
64 \r
65   private String url;\r
66   private TestHeadResult result;\r
67   private Date startTime;\r
68   private Date endTime;\r
69 \r
70   public TestHeadCallable(\r
71       int timeoutInMillis,\r
72       String httpMethod,\r
73       Map<String, String> headers,\r
74       Map<String, Object> vthInput,\r
75       TestHead testHead,\r
76       String activityId,\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
86 \r
87     this.mongoOperation = mongoOperation;\r
88 \r
89     // Generate the url after the test head is set.\r
90     this.url = generateUrl();\r
91   }\r
92 \r
93   @Override\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
100           simulateVTH(\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
104 \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
109       return result;\r
110     }\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
116 \r
117     // add api key to headers if required\r
118     setApiKeyIfEnabled();\r
119 \r
120     //TODO RG Added to slow Execution\r
121     //Thread.sleep(60000);\r
122 \r
123     try {\r
124       switch (httpMethod.toLowerCase()) {\r
125         case "post":\r
126           response =\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
131           break;\r
132         case "get":\r
133           response =\r
134               timeoutInMillis > 0\r
135                   ? RequestUtility.getSync(url, headers, timeoutInMillis, false)\r
136                   : RequestUtility.getSync(url, headers, false);\r
137           break;\r
138         default:\r
139           throw new RuntimeException();\r
140       }\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
144 \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
151 \r
152       logger.info(logPrefix + "There was an error calling the test head.");\r
153     }\r
154 \r
155     testExecution.getTestHeadResults().add(result);\r
156     saveResult(testExecution);\r
157     return result;\r
158   }\r
159 \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
163     }\r
164   }\r
165 \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
172     }\r
173     return testHead.getHostname() + ":" + testHead.getPort() + resource;\r
174   }\r
175 \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
179 \r
180     return new TestHeadResult(\r
181         testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, statusCodeError, requestContent, error, startTime, endTime);\r
182   }\r
183 \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
188     try {\r
189       res = Convert.jsonToMap(sRes);\r
190     } catch (Exception e) {\r
191       res = new HashMap<>();\r
192       res.put("response", sRes);\r
193     }\r
194 \r
195     TestHeadRequest requestContent = new TestHeadRequest(HeadersUtility.maskAuth(headers), body);\r
196 \r
197     return new TestHeadResult(\r
198         testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, response.getStatusLine().getStatusCode(), requestContent, res, startTime, endTime);\r
199   }\r
200 \r
201   private TestHeadResult generateResult(Map<String, Object> res) {\r
202 \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
205 \r
206     return new TestHeadResult(\r
207         testHead.get_id(), testHead.getTestHeadName(), testHead.getGroupId(), activityId, 200, requestContent, res, startTime, endTime);\r
208   }\r
209 \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
222           String.format(\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
227     }\r
228   }\r
229 \r
230   private Map<String, Object> simulateVTH(String activityId, Map<String, Object> simulationVth) {\r
231     int delay = 0;\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
237       }\r
238     } else {\r
239       return null;\r
240     }\r
241 \r
242     if (simulationVth.containsKey("delay")) {\r
243       Object obj = simulationVth.get("delay");\r
244       if (obj instanceof Integer) {\r
245         delay = (int) obj;\r
246       }\r
247     }\r
248 \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
253       }\r
254     }\r
255     logger.info(logPrefix + "Starting simulated call to test head.");\r
256 \r
257     try {\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
262       return null;\r
263     }\r
264     logger.info(logPrefix + "Finished simulating call to test head.");\r
265     return response;\r
266   }\r
267 }\r