Merge from ecomp 718fd196 - Ext. Services Simulator
[vid.git] / vid-ext-services-simulator / src / main / java / org / onap / simulator / controller / SimulatorController.java
1 package org.onap.simulator.controller;
2
3 import com.fasterxml.jackson.databind.DeserializationFeature;
4 import com.fasterxml.jackson.databind.ObjectMapper;
5 import com.google.gson.Gson;
6 import org.mockserver.integration.ClientAndServer;
7 import org.mockserver.matchers.MatchType;
8 import org.mockserver.matchers.Times;
9 import org.mockserver.model.HttpRequest;
10 import org.mockserver.model.HttpResponse;
11 import org.mockserver.model.JsonBody;
12 import org.onap.simulator.db.entities.Function;
13 import org.onap.simulator.db.entities.User;
14 import org.onap.simulator.errorHandling.VidSimulatorException;
15 import org.onap.simulator.model.SimulatorRequestResponseExpectation;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18 import org.springframework.core.io.ClassPathResource;
19 import org.springframework.core.io.Resource;
20 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
21 import org.springframework.core.io.support.PropertiesLoaderUtils;
22 import org.springframework.core.io.support.ResourcePatternResolver;
23 import org.springframework.http.*;
24 import org.springframework.stereotype.Component;
25 import org.springframework.web.bind.annotation.*;
26 import org.springframework.web.client.HttpClientErrorException;
27 import org.springframework.web.client.RestTemplate;
28 import org.springframework.web.servlet.View;
29
30 import javax.annotation.PostConstruct;
31 import javax.annotation.PreDestroy;
32 import javax.persistence.EntityManager;
33 import javax.persistence.EntityManagerFactory;
34 import javax.persistence.Persistence;
35 import javax.persistence.TypedQuery;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38 import java.io.*;
39 import java.net.URI;
40 import java.net.URISyntaxException;
41 import java.net.URLEncoder;
42 import java.nio.file.Files;
43 import java.nio.file.Path;
44 import java.nio.file.Paths;
45 import java.util.*;
46 import java.util.stream.Collectors;
47 import java.util.stream.Stream;
48
49 import static org.mockserver.integration.ClientAndServer.startClientAndServer;
50 import static org.mockserver.matchers.Times.exactly;
51 import static org.mockserver.model.JsonBody.json;
52
53 @RestController
54 @Component
55 public class SimulatorController {
56
57     private static final Times DEFAULT_NUMBER_OF_TIMES = Times.unlimited();
58     private ClientAndServer mockServer;
59     private String mockServerProtocol;
60     private String mockServerHost;
61     private Integer mockServerPort;
62     private Boolean enablePresetRegistration;
63     private Boolean enableJPA;
64     private volatile boolean isInitialized = false;
65
66     private EntityManager entityManager;
67     private EntityManagerFactory entityManagerFactory;
68
69
70     private static final Logger logger = LoggerFactory.getLogger(SimulatorController.class);
71
72     @PostConstruct
73     public void init(){
74         logger.info("Starting VID Simulator....");
75         setProperties();
76         mockServer = startClientAndServer(mockServerPort);
77         presetRegister();
78         try {
79             initJPA();
80         } catch (RuntimeException e) {
81             isInitialized = false;
82             logger.error("Error during the JPA initialization:", e);
83             return;
84         }
85         isInitialized = true;
86         logger.info("VID Simulator started successfully");
87     }
88
89     private void initJPA() {
90         if (enableJPA) {
91             entityManagerFactory = Persistence.createEntityManagerFactory("vid");
92             entityManager = entityManagerFactory.createEntityManager();
93         }
94     }
95
96     @PreDestroy
97     public void tearDown(){
98         logger.info("Stopping VID Simulator....");
99         entityManager.close();
100         entityManagerFactory.close();
101         isInitialized = false;
102         mockServer.stop(false);
103     }
104
105
106     private void presetRegister() {
107         //Checking if set
108         if (enablePresetRegistration == null || !enablePresetRegistration){
109             logger.info("Preset registration property is false or not set - skipping preset registration...");
110             return;
111         }
112         ClassLoader cl = this.getClass().getClassLoader();
113         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
114         List<Path> resources = new ArrayList<>();
115         try {
116             File presetDir = resolver.getResource("/preset_registration/").getFile();
117             if (presetDir.exists() && presetDir.isDirectory()) {
118                 try (Stream<Path> files = Files.walk(Paths.get(presetDir.getPath()))) {
119                     resources = files
120                             .filter(p -> p.toString().endsWith(".json"))
121                             .collect(Collectors.toList());
122                 }
123             } else {
124                 logger.error("preset_registration directory is not exists");
125             }
126         } catch (IOException e) {
127             logger.error("Error performing preset registration, error: ", e);
128             return;
129         }
130         logger.info("Starting preset registrations, number of requests: {}", resources.size());
131         for (Path resource: resources){
132             String content;
133             try (Scanner scanner = new Scanner(resource).useDelimiter("\\Z")){
134                 content = scanner.next();
135             } catch (IOException e){
136                 logger.error("Error reading preset registration file {}, skipping to next one. Error: ", resource.getFileName(), e);
137                 continue;
138             }
139             //register the preset request
140             try {
141                 register(content);
142             } catch (VidSimulatorException e) {
143                 logger.error("Error proceeding preset registration file {},skipping to next one. Check if the JSON is in correct format. Error: ", resource.getFileName(), e);
144             }
145         }
146     }
147
148
149
150     private void setProperties() {
151         Resource resource = new ClassPathResource("simulator.properties");
152         Properties props = new Properties();
153         try {
154             props = PropertiesLoaderUtils.loadProperties(resource);
155         } catch (IOException e) {
156             logger.error("Error loading simulator properties, error: ", e);
157             return;
158         }
159         logger.info("Simulator properties are {}", props);
160         mockServerProtocol = (String)props.get("simulator.mockserver.protocol");
161         mockServerHost = (String)props.get("simulator.mockserver.host");
162         mockServerPort = Integer.parseInt((String)props.get("simulator.mockserver.port"));
163         enablePresetRegistration = Boolean.parseBoolean((String)props.get("simulator.enablePresetRegistration"));
164         enableJPA = Boolean.parseBoolean((String)props.get("simulator.enableCentralizedRoleAccess"));
165     }
166
167     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.POST)
168     public @ResponseBody
169     ResponseEntity registerRequest(HttpServletRequest request, @RequestBody String expectation) {
170         try {
171             register(expectation);
172         } catch (VidSimulatorException e) {
173             return new ResponseEntity<>("Registration failure! Error: "+e.getMessage(),HttpStatus.BAD_REQUEST);
174         }
175         return new ResponseEntity<>("Registration successful!",HttpStatus.OK);
176     }
177
178     @RequestMapping(value = {"/echo"}, method = RequestMethod.GET)
179     ResponseEntity echo(HttpServletRequest request) {
180         return isInitialized ? new ResponseEntity<>("",HttpStatus.OK) : new ResponseEntity<>("",HttpStatus.SERVICE_UNAVAILABLE);
181     }
182
183     @RequestMapping(value = {"/retrieveRecordedRequests"}, method = RequestMethod.GET)
184     public List<HttpRequest> retrieveRecordedRequests() {
185         return Arrays.asList(mockServer.retrieveRecordedRequests(null));
186     }
187
188     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.DELETE)
189     @ResponseStatus(value = HttpStatus.OK)
190     public void wipeOutAllExpectations() {
191         mockServer.reset();
192     }
193
194     private void register(String expectation) throws VidSimulatorException{
195         ObjectMapper mapper = new ObjectMapper()
196                 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
197
198         SimulatorRequestResponseExpectation[] expectationModels;
199         try {
200             expectationModels = mapper.readValue(expectation, SimulatorRequestResponseExpectation[].class);
201         } catch (IOException e) {
202             logger.error("Couldn't deserialize register expectation {}, error:", expectation, e);
203             throw new VidSimulatorException(e.getMessage());
204         }
205
206         for (SimulatorRequestResponseExpectation expectationModel : expectationModels) {
207             logger.info("Proceeding registration request: {}", expectationModel);
208             register(expectationModel);
209         }
210     }
211
212     //*******portal role access simulator (added by ag137v)
213
214     @RequestMapping(value = {"/ecompportal_att/auxapi//{ver}/user/*", "/ONAPPORTAL/auxapi//{ver}/user/*"}, method = RequestMethod.GET)
215     public @ResponseBody
216     ResponseEntity auxapiGetUser(HttpServletRequest request) {
217         if (!enableJPA) {
218             return new ResponseEntity<>("Centralized Role Access is disabled", HttpStatus.SERVICE_UNAVAILABLE);
219         }
220         entityManager.clear();
221         String reqUri = request.getRequestURI();
222         String userName = reqUri.substring(reqUri.lastIndexOf('/') + 1);
223         TypedQuery<User> userQuery = entityManager.createQuery("select u from fn_user u where u.loginId = :userName", User.class);
224         userQuery.setParameter("userName", userName);
225         User user = userQuery.getSingleResult();
226
227         Gson g = new Gson();
228         String jsonString = g.toJson(user);
229
230         return new ResponseEntity<>(jsonString, HttpStatus.OK);
231
232     }
233
234     @RequestMapping(value = {"/ecompportal_att/auxapi//{ver}/functions", "/ONAPPORTAL/auxapi//{ver}/functions"}, method = RequestMethod.GET)
235     public @ResponseBody
236     ResponseEntity auxapiGetFunctions(HttpServletRequest request) {
237         if (!enableJPA) {
238             return new ResponseEntity<>("Centralized Role Access is disabled", HttpStatus.SERVICE_UNAVAILABLE);
239         }
240         TypedQuery<Function> userQuery = entityManager.createQuery("select f from fn_function f", Function.class);
241         List<Function> functions = userQuery.getResultList();
242         Gson g = new Gson();
243         String jsonString = g.toJson(functions);
244
245         return new ResponseEntity<>(jsonString, HttpStatus.OK);
246
247     }
248     //*******portal role access simulator end
249
250
251     @RequestMapping(value = {"/**"})
252     public ResponseEntity redirectToMockServer(HttpServletRequest request, HttpServletResponse response) throws IOException {
253         //This is needed to allow POST redirect - see http://www.baeldung.com/spring-redirect-and-forward
254         request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
255
256         //Building the redirect URL
257 //        String restOfTheUrl = (String) request.getAttribute(
258 //                HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
259         String requestUri = URLEncoder.encode(request.getRequestURI(), "UTF-8");
260         requestUri = requestUri.replaceAll("%2F", "/");
261         String restOfTheUrl = requestUri.replaceFirst(request.getContextPath(), "");
262
263         StringBuilder sb = new StringBuilder();
264         sb.append(mockServerProtocol).append("://").append(mockServerHost).append(":").append(mockServerPort).append(restOfTheUrl);
265         String queryString = request.getQueryString();
266         if (queryString != null){
267             sb.append("?").append(queryString);
268         }
269         String redirectUrl = sb.toString();
270         logger.info("Redirecting the request to : {}", redirectUrl);
271
272         URI uri;
273         try {
274             uri = new URI("http", null, "localhost", 1080, restOfTheUrl, request.getQueryString(), null);
275         } catch (URISyntaxException e) {
276             logger.error("Error during proxying request {}, error: ", request.getRequestURI(), e.getMessage());
277             return new ResponseEntity(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
278         }
279         RestTemplate restTemplate = new RestTemplate();
280         //Preparing the headers
281         HttpHeaders headers = new HttpHeaders();
282         Enumeration<String> headerNames =  request.getHeaderNames();
283         while (headerNames.hasMoreElements()){
284             String headerToSet = headerNames.nextElement();
285             headers.set(headerToSet, request.getHeader(headerToSet));
286         }
287         HttpEntity<String> proxyRequest;
288         if ("POST".equalsIgnoreCase(request.getMethod()))
289         {
290             String body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
291             proxyRequest = new HttpEntity<>(body, headers);
292         } else {
293             proxyRequest = new HttpEntity<>(headers);
294         }
295
296         ResponseEntity<String> responseEntity;
297         try {
298             responseEntity =
299                     restTemplate.exchange(uri, HttpMethod.resolve(request.getMethod()), proxyRequest, String.class);
300         } catch (HttpClientErrorException exception) {
301             responseEntity = new ResponseEntity<>(exception.getResponseBodyAsString(), exception.getStatusCode());
302         }
303
304         return responseEntity;
305     }
306
307     private void register(SimulatorRequestResponseExpectation expectationModel) throws VidSimulatorException{
308         //Setting request according to what is passed
309         HttpRequest request = HttpRequest.request();
310         String id = expectationModel.getSimulatorRequest().getId();
311         if (id != null) {
312             request.withHeader("x-simulator-id", id);
313         }
314
315         if (expectationModel.getSimulatorRequest().getHeaders()!=null) {
316             expectationModel.getSimulatorRequest().getHeaders().forEach(
317                     request::withHeader);
318         }
319
320         String method = expectationModel.getSimulatorRequest().getMethod();
321         if (method != null) {
322             request.withMethod(method);
323         }
324         String path = expectationModel.getSimulatorRequest().getPath();
325         if (path != null) {
326             request.withPath(path);
327         }
328         String body = expectationModel.getSimulatorRequest().getBody();
329         if (body != null) {
330             if (expectationModel.getSimulatorRequest().getStrict()) {
331                 request.withBody(json(body, MatchType.STRICT));
332             } else {
333                 request.withBody(new JsonBody(body));
334             }
335         }
336
337         //Queryparams
338         final Map<String, List<String>> queryParams = expectationModel.getSimulatorRequest().getQueryParams();
339         if (queryParams != null){
340             String[] arr = new String[0];
341             queryParams.forEach((key, value) -> request.withQueryStringParameter(key, value.toArray(arr)));
342         }
343
344         //Setting response according to what is passed
345         HttpResponse response = HttpResponse.response();
346         Integer responseCode = expectationModel.getSimulatorResponse().getResponseCode();
347         if (responseCode != null) {
348             response.withStatusCode(responseCode);
349         } else {
350             logger.error("Invalid registration - response code cannot be empty");
351             throw new VidSimulatorException("Invalid registration - response code cannot be empty");
352         }
353
354         String respBody = expectationModel.getSimulatorResponse().getBody();
355         if (respBody != null) {
356             response.withBody(respBody);
357         }
358
359         String file = expectationModel.getSimulatorResponse().getFile();
360         if (file != null) {
361             response.withBody(loadFileString(file));
362         }
363
364         Map<String, String> responseHeaders = expectationModel.getSimulatorResponse().getResponseHeaders();
365         if (responseHeaders != null) {
366             responseHeaders.forEach(response::withHeader);
367         }
368
369         Times numberOfTimes = getExpectationNumberOfTimes(expectationModel);
370
371         if (expectationModel.getMisc().getReplace()) {
372             logger.info("Unregistering request expectation, if previously set, request: {}", expectationModel.getSimulatorRequest());
373             mockServer.clear(request);
374         }
375
376         mockServer
377                 .when(request, numberOfTimes).respond(response);
378     }
379
380
381     private byte[] loadFileString(String filePath) {
382         byte[] bytes = null;
383         File file = null;
384         try {
385             file = new ClassPathResource("download_files/" + filePath).getFile();
386             try(DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.getPath())))) {
387                 bytes = new byte[(int)file.length()];
388                 dataInputStream.readFully(bytes);
389             }
390         } catch (FileNotFoundException e) {
391             logger.error("File not found for file:" + filePath);
392             e.printStackTrace();
393         } catch (IOException e) {
394             logger.error("Error reading file:" + filePath);
395             e.printStackTrace();
396         }
397
398         return bytes;
399     }
400     private Times getExpectationNumberOfTimes(SimulatorRequestResponseExpectation expectationModel) {
401         Integer expectationModelNumberOfTimes = expectationModel.getMisc().getNumberOfTimes();
402         Times effectiveNumberOfTimes;
403         if (expectationModelNumberOfTimes == null || expectationModelNumberOfTimes < 0) {
404             effectiveNumberOfTimes = DEFAULT_NUMBER_OF_TIMES;
405         } else {
406             effectiveNumberOfTimes = exactly(expectationModelNumberOfTimes);
407         }
408         return effectiveNumberOfTimes;
409     }
410 }