Introduced mocked SO workflows in VID FE
[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 org.mockserver.integration.ClientAndServer;
6 import org.mockserver.matchers.Times;
7 import org.mockserver.model.HttpRequest;
8 import org.mockserver.model.HttpResponse;
9
10 import org.mockserver.model.JsonBody;
11 import org.onap.simulator.errorHandling.VidSimulatorException;
12 import org.onap.simulator.model.SimulatorRequestResponseExpectation;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15 import org.springframework.core.io.ClassPathResource;
16 import org.springframework.core.io.Resource;
17 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
18 import org.springframework.core.io.support.PropertiesLoaderUtils;
19 import org.springframework.core.io.support.ResourcePatternResolver;
20 import org.springframework.http.HttpStatus;
21 import org.springframework.http.ResponseEntity;
22 import org.springframework.stereotype.Component;
23 import org.springframework.web.bind.annotation.*;
24 import org.springframework.web.servlet.HandlerMapping;
25 import org.springframework.web.servlet.View;
26
27 import javax.annotation.PostConstruct;
28 import javax.annotation.PreDestroy;
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31 import java.io.*;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.util.*;
36 import java.util.stream.Collectors;
37
38 import static org.mockserver.integration.ClientAndServer.startClientAndServer;
39 import static org.mockserver.matchers.Times.exactly;
40
41 @RestController
42 @Component
43 public class SimulatorController {
44
45     private static final Times DEFAULT_NUMBER_OF_TIMES = Times.unlimited();
46     private ClientAndServer mockServer;
47     private String mockServerProtocol;
48     private String mockServerHost;
49     private Integer mockServerPort;
50     private Boolean enablePresetRegistration;
51     private volatile boolean isInitialized = false;
52
53     Logger logger = LoggerFactory.getLogger(SimulatorController.class);
54
55     @PostConstruct
56     public void init(){
57         logger.info("Starting VID Simulator....");
58         setProperties();
59         mockServer = startClientAndServer(mockServerPort);
60         presetRegister();
61         isInitialized = true;
62         logger.info("VID Simulator started successfully");
63     }
64
65     @PreDestroy
66     public void tearDown(){
67         logger.info("Stopping VID Simulator....");
68         isInitialized = false;
69         mockServer.stop();
70     }
71
72
73     private void presetRegister() {
74         //Checking if set
75         if (enablePresetRegistration == null || !enablePresetRegistration){
76             logger.info("Preset registration property is false or not set - skipping preset registration...");
77             return;
78         }
79         ClassLoader cl = this.getClass().getClassLoader();
80         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl);
81         List<Path> resources = new ArrayList<>();
82         try {
83             File presetDir = resolver.getResource("/preset_registration/").getFile();
84             if (presetDir.exists() && presetDir.isDirectory()) {
85                 resources = Files.walk(Paths.get(presetDir.getPath()))
86                         .filter(p -> p.toString().endsWith(".json"))
87                         .collect(Collectors.toList());
88             } else {
89                 logger.error("preset_registration directory is not exists");
90             }
91         } catch (IOException e) {
92             logger.error("Error performing preset registration, error: ", e);
93             return;
94         }
95         logger.info("Starting preset registrations, number of requests: {}", resources.size());
96         for (Path resource: resources){
97             String content;
98             try {
99                 content = new Scanner(resource).useDelimiter("\\Z").next();
100             } catch (IOException e){
101                 logger.error("Error reading preset registration file {}, skipping to next one. Error: ", resource.getFileName(), e);
102                 continue;
103             }
104             //register the preset request
105             try {
106                 register(content);
107             } catch (VidSimulatorException e) {
108                 logger.error("Error proceeding preset registration file {},skipping to next one. Check if the JSON is in correct format. Error: ", resource.getFileName(), e);
109             }
110         }
111     }
112
113
114
115     private void setProperties() {
116         Resource resource = new ClassPathResource("simulator.properties");
117         Properties props = new Properties();
118         try {
119             props = PropertiesLoaderUtils.loadProperties(resource);
120         } catch (IOException e) {
121             logger.error("Error loading simulator properties, error: ", e);
122             return;
123         }
124         logger.info("Simulator properties are {}", props);
125         mockServerProtocol = (String)props.get("simulator.mockserver.protocol");
126         mockServerHost = (String)props.get("simulator.mockserver.host");
127         mockServerPort = Integer.parseInt((String)props.get("simulator.mockserver.port"));
128         enablePresetRegistration = Boolean.parseBoolean((String)props.get("simulator.enablePresetRegistration"));
129     }
130
131     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.POST)
132     public @ResponseBody
133     ResponseEntity registerRequest(HttpServletRequest request, @RequestBody String expectation) {
134         try {
135             register(expectation);
136         } catch (VidSimulatorException e) {
137             return new ResponseEntity<>("Registration failure! Error: "+e.getMessage(),HttpStatus.BAD_REQUEST);
138         }
139         return new ResponseEntity<>("Registration successful!",HttpStatus.OK);
140     }
141
142     @RequestMapping(value = {"/echo"}, method = RequestMethod.GET)
143     ResponseEntity echo(HttpServletRequest request) {
144         return isInitialized ? new ResponseEntity<>("",HttpStatus.OK) : new ResponseEntity<>("",HttpStatus.SERVICE_UNAVAILABLE);
145     }
146
147 //    @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.GET)
148 //    public ResponseEntity<String> getAllRegisteredRequests() throws JsonProcessingException {
149 //        final Expectation[] expectations = mockServer.retrieveExistingExpectations(null);
150 //        return new ResponseEntity<>(new ObjectMapper()
151 //                .configure(SerializationFeature.INDENT_OUTPUT, true)
152 //                .writeValueAsString(expectations), HttpStatus.OK);
153 //    }
154
155     @RequestMapping(value = {"/registerToVidSimulator"}, method = RequestMethod.DELETE)
156     @ResponseStatus(value = HttpStatus.OK)
157     public void wipeOutAllExpectations() {
158         mockServer.reset();
159     }
160
161     private void register(String expectation) throws VidSimulatorException{
162         ObjectMapper mapper = new ObjectMapper()
163                 .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
164
165         SimulatorRequestResponseExpectation[] expectationModels;
166         try {
167             expectationModels = mapper.readValue(expectation, SimulatorRequestResponseExpectation[].class);
168         } catch (IOException e) {
169             logger.error("Couldn't deserialize register expectation {}, error:", expectation, e);
170             throw new VidSimulatorException(e.getMessage());
171         }
172
173         for (SimulatorRequestResponseExpectation expectationModel : expectationModels) {
174             logger.info("Proceeding registration request: {}", expectationModel);
175             register(expectationModel);
176         }
177     }
178
179     @RequestMapping(value = {"/**"})
180     public String redirectToMockServer(HttpServletRequest request, HttpServletResponse response) {
181         //Currently, the easiest logic is redirecting
182
183         //This is needed to allow POST redirect - see http://www.baeldung.com/spring-redirect-and-forward
184         request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
185
186         //Building the redirect URL
187         String restOfTheUrl = (String) request.getAttribute(
188                 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
189
190         //TODO encode only characters like spaces, not slashes
191        /* try {
192             restOfTheUrl = URLEncoder.encode(restOfTheUrl, "UTF-8");
193             restOfTheUrl = restOfTheUrl.replaceAll("%2F", "/");
194         } catch (UnsupportedEncodingException e) {
195             e.printStackTrace();
196         }*/
197
198         StringBuilder sb = new StringBuilder();
199         sb.append(mockServerProtocol+"://"+mockServerHost+":"+mockServerPort+"/"+restOfTheUrl);
200         String queryString = request.getQueryString();
201         if (queryString != null){
202             sb.append("?").append(queryString);
203         }
204         String redirectUrl = sb.toString();
205         logger.info("Redirecting the request to : {}", redirectUrl);
206         return ("redirect:"+redirectUrl);
207
208         //This was a try to setup a proxy instead of redirect
209         //Abandoned this direction when trying to return the original HTTP error code which was registered to mock server,  instead of wrapped up HTTP 500.
210
211        /* String restOfTheUrl = "/"+(String) request.getAttribute(
212                 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
213         URI uri = null;
214         try {
215             uri = new URI("http", null, "localhost", 1080, restOfTheUrl, request.getQueryString(), null);
216         } catch (URISyntaxException e) {
217             logger.error("Error during proxying request {}, error: ", request.getRequestURI(), e.getMessage());
218             return new ResponseEntity(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
219         }
220         RestTemplate restTemplate = new RestTemplate();
221         //Preparing the headers
222         HttpHeaders headers = new HttpHeaders();
223         Enumeration<String> headerNames =  request.getHeaderNames();
224         while (headerNames.hasMoreElements()){
225             String headerToSet = headerNames.nextElement();
226             headers.set(headerToSet, request.getHeader(headerToSet));
227         }
228
229         ResponseEntity<String> responseEntity =
230                 restTemplate.exchange(uri, HttpMethod.resolve(request.getMethod()), new HttpEntity<String>(body, headers), String.class);
231         
232         return responseEntity;*/
233     }
234
235     private void register(SimulatorRequestResponseExpectation expectationModel) throws VidSimulatorException{
236         //Setting request according to what is passed
237         HttpRequest request = HttpRequest.request();
238         String id = expectationModel.getSimulatorRequest().getId();
239         if (id != null) {
240             request.withHeader("x-simulator-id", id);
241         }
242         String method = expectationModel.getSimulatorRequest().getMethod();
243         if (method != null) {
244             request.withMethod(method);
245         }
246         String path = expectationModel.getSimulatorRequest().getPath();
247         if (path != null) {
248             request.withPath(path);
249         }
250         String body = expectationModel.getSimulatorRequest().getBody();
251         if (body != null) {
252             request.withBody(new JsonBody(body));
253         }
254
255         //Queryparams
256         final Map<String, List<String>> queryParams = expectationModel.getSimulatorRequest().getQueryParams();
257         if (queryParams != null){
258             String[] arr = new String[0];
259             queryParams.entrySet().stream().forEach(x -> {
260                 request.withQueryStringParameter(x.getKey(), x.getValue().toArray(arr));
261             });
262         }
263
264         //Setting response according to what is passed
265         HttpResponse response = HttpResponse.response();
266         Integer responseCode = expectationModel.getSimulatorResponse().getResponseCode();
267         if (responseCode != null) {
268             response.withStatusCode(responseCode);
269         } else {
270             logger.error("Invalid registration - response code cannot be empty");
271             throw new VidSimulatorException("Invalid registration - response code cannot be empty");
272         }
273
274         String respBody = expectationModel.getSimulatorResponse().getBody();
275         if (respBody != null) {
276             response.withBody(respBody);
277         }
278
279         String file = expectationModel.getSimulatorResponse().getFile();
280         if (file != null) {
281             response.withBody(loadFileString(file));
282         }
283
284         Map<String, String> responseHeaders = expectationModel.getSimulatorResponse().getResponseHeaders();
285         if (responseHeaders != null) {
286             responseHeaders.forEach(response::withHeader);
287         }
288
289         Times numberOfTimes = getExpectationNumberOfTimes(expectationModel);
290
291         if (expectationModel.getMisc().getReplace()) {
292             logger.info("Unregistering request expectation, if previously set, request: {}", expectationModel.getSimulatorRequest());
293             mockServer.clear(request);
294         }
295
296         mockServer
297                 .when(request, numberOfTimes).respond(response);
298     }
299
300
301     private byte[] loadFileString(String filePath) {
302         byte[] bytes = null;
303         try {
304             File file = new ClassPathResource("download_files/" + filePath).getFile();
305             bytes = new byte[(int)file.length()];
306             DataInputStream dataInputStream = null;
307
308             dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.getPath())));
309             dataInputStream.readFully(bytes);
310             dataInputStream.close();
311         } catch (FileNotFoundException e) {
312             logger.error("File not found for file:" + filePath);
313             e.printStackTrace();
314         } catch (IOException e) {
315             logger.error("Error reading file:" + filePath);
316             e.printStackTrace();
317         }
318
319         return bytes;
320     }
321     private Times getExpectationNumberOfTimes(SimulatorRequestResponseExpectation expectationModel) {
322         Integer expectationModelNumberOfTimes = expectationModel.getMisc().getNumberOfTimes();
323         Times effectiveNumberOfTimes;
324         if (expectationModelNumberOfTimes == null || expectationModelNumberOfTimes < 0) {
325             effectiveNumberOfTimes = DEFAULT_NUMBER_OF_TIMES;
326         } else {
327             effectiveNumberOfTimes = exactly(expectationModelNumberOfTimes);
328         }
329         return effectiveNumberOfTimes;
330     }
331 }