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