io.github.microcks.web.DynamicMockRestController Maven / Gradle / Ivy
/*
* Copyright The Microcks Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.microcks.web;
import io.github.microcks.domain.GenericResource;
import io.github.microcks.domain.Operation;
import io.github.microcks.domain.Service;
import io.github.microcks.domain.ServiceType;
import io.github.microcks.event.MockInvocationEvent;
import io.github.microcks.repository.GenericResourceRepository;
import io.github.microcks.repository.ServiceRepository;
import io.github.microcks.util.el.EvaluableRequest;
import io.github.microcks.util.el.TemplateEngine;
import io.github.microcks.util.el.TemplateEngineFactory;
import org.bson.Document;
import org.bson.json.JsonParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.UriUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* This is the controller for Dynamic mocks in Microcks.
* @author laurent
*/
@org.springframework.web.bind.annotation.RestController
@RequestMapping("/dynarest")
public class DynamicMockRestController {
/** A simple logger for diagnostic messages. */
private static Logger log = LoggerFactory.getLogger(DynamicMockRestController.class);
public static final String ID_FIELD = "id";
@Autowired
ServiceRepository serviceRepository;
@Autowired
GenericResourceRepository genericResourceRepository;
@Autowired
private ApplicationContext applicationContext;
@Value("${mocks.enable-invocation-stats}")
private final Boolean enableInvocationStats = null;
@RequestMapping(value = "/{service}/{version}/{resource}", method = RequestMethod.POST, produces = "application/json")
public ResponseEntity createResource(
@PathVariable("service") String serviceName,
@PathVariable("version") String version,
@PathVariable("resource") String resource,
@RequestParam(value="delay", required=false) Long delay,
@RequestBody(required=true) String body,
HttpServletRequest request
) {
log.debug("Creating a new resource '{}' for service '{}-{}'", resource, serviceName, version);
long startTime = System.currentTimeMillis();
serviceName = sanitizeServiceName(serviceName);
MockContext mockContext = getMockContext(serviceName, version, "POST /" + resource);
if (mockContext != null) {
Document document = null;
GenericResource genericResource = null;
try {
// Try parsing body payload that should be json.
document = Document.parse(body);
// Now create a generic resource.
genericResource = new GenericResource();
genericResource.setServiceId(mockContext.service.getId());
genericResource.setPayload(document);
genericResource = genericResourceRepository.save(genericResource);
} catch (JsonParseException jpe) {
// Return a 422 code : unprocessable entity.
return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
}
// Append id and wait if specified before returning.
document.append(ID_FIELD, genericResource.getId());
waitForDelay(startTime, delay, mockContext);
return new ResponseEntity<>(document.toJson(), HttpStatus.CREATED);
}
// Return a 400 code : bad request.
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/{service}/{version}/{resource}", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity findResources(
@PathVariable("service") String serviceName,
@PathVariable("version") String version,
@PathVariable("resource") String resource,
@RequestParam(value="page", required=false, defaultValue="0") int page,
@RequestParam(value="size", required=false, defaultValue="20") int size,
@RequestParam(value="delay", required=false) Long delay,
@RequestBody(required=false) String body,
HttpServletRequest request
) {
log.debug("Find resources '{}' for service '{}-{}'", resource, serviceName, version);
long startTime = System.currentTimeMillis();
serviceName = sanitizeServiceName(serviceName);
// Build the encoded URI fragment to retrieve simple resourcePath.
String requestURI = request.getRequestURI();
String serviceAndVersion = "/" + UriUtils.encodeFragment(serviceName, "UTF-8") + "/" + version;
String resourcePath = requestURI.substring(requestURI.indexOf(serviceAndVersion) + serviceAndVersion.length());
MockContext mockContext = getMockContext(serviceName, version, "GET /" + resource);
if (mockContext != null) {
List genericResources = null;
if (body == null) {
genericResources = genericResourceRepository.findByServiceId(mockContext.service.getId(), PageRequest.of(page, size));
} else {
genericResources = genericResourceRepository.findByServiceIdAndJSONQuery(mockContext.service.getId(), body);
}
// Prepare templating support with evaluable request and engine.
EvaluableRequest evaluableRequest = MockControllerCommons.buildEvaluableRequest(body, resourcePath, request);
TemplateEngine engine = TemplateEngineFactory.getTemplateEngine();
// Transform and collect resources.
List resources = genericResources.stream()
.map(genericResource -> MockControllerCommons.renderResponseContent(
evaluableRequest, engine,
transformToResourceJSON(genericResource)
))
.collect(Collectors.toList());
// Wait if specified before returning.
waitForDelay(startTime, delay, mockContext);
MockControllerCommons.waitForDelay(startTime, delay);
return new ResponseEntity<>(formatToJSONArray(resources), HttpStatus.OK);
}
// Return a 400 code : bad request.
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/{service}/{version}/{resource}/{resourceId}", method = RequestMethod.GET,
produces = "application/json")
public ResponseEntity getResource(
@PathVariable("service") String serviceName,
@PathVariable("version") String version,
@PathVariable("resource") String resource,
@PathVariable("resourceId") String resourceId,
@RequestParam(value="delay", required=false) Long delay,
HttpServletRequest request
) {
log.debug("Get resource '{}:{}' for service '{}-{}'", resource, resourceId, serviceName, version);
long startTime = System.currentTimeMillis();
serviceName = sanitizeServiceName(serviceName);
// Build the encoded URI fragment to retrieve simple resourcePath.
String requestURI = request.getRequestURI();
String serviceAndVersion = "/" + UriUtils.encodeFragment(serviceName, "UTF-8") + "/" + version;
String resourcePath = requestURI.substring(requestURI.indexOf(serviceAndVersion) + serviceAndVersion.length());
MockContext mockContext = getMockContext(serviceName, version, "GET /" + resource + "/:id");
if (mockContext != null) {
// Get the requested generic resource.
GenericResource genericResource = genericResourceRepository.findById(resourceId).orElse(null);
// Wait if specified before returning.
waitForDelay(startTime, delay, mockContext);
if (genericResource != null) {
// Prepare templating support with evaluable request and engine.
EvaluableRequest evaluableRequest = MockControllerCommons.buildEvaluableRequest(null, resourcePath, request);
TemplateEngine engine = TemplateEngineFactory.getTemplateEngine();
// Return the resource as well as a 200 code.
return new ResponseEntity<>(MockControllerCommons.renderResponseContent(
evaluableRequest, engine, transformToResourceJSON(genericResource)), HttpStatus.OK);
} else {
// Return a 404 code : not found.
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
// Return a 400 code : bad request.
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/{service}/{version}/{resource}/{resourceId}", method = RequestMethod.PUT,
produces = "application/json")
public ResponseEntity updateResource(
@PathVariable("service") String serviceName,
@PathVariable("version") String version,
@PathVariable("resource") String resource,
@PathVariable("resourceId") String resourceId,
@RequestParam(value="delay", required=false) Long delay,
@RequestBody(required=true) String body,
HttpServletRequest request
) {
log.debug("Update resource '{}:{}' for service '{}-{}'", resource, resourceId, serviceName, version);
long startTime = System.currentTimeMillis();
serviceName = sanitizeServiceName(serviceName);
MockContext mockContext = getMockContext(serviceName, version, "PUT /" + resource + "/:id");
if (mockContext != null) {
// Get the requested generic resource.
GenericResource genericResource = genericResourceRepository.findById(resourceId).orElse(null);
if (genericResource != null) {
Document document = null;
try {
// Try parsing body payload that should be json.
document = Document.parse(body);
document.remove(ID_FIELD);
// Now update the generic resource payload.
genericResource.setPayload(document);
genericResourceRepository.save(genericResource);
} catch (JsonParseException jpe) {
// Return a 422 code : unprocessable entity.
return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
}
// Wait if specified before returning.
waitForDelay(startTime, delay, mockContext);
// Return the updated resource as well as a 200 code.
return new ResponseEntity<>(transformToResourceJSON(genericResource), HttpStatus.OK);
} else {
// Wait if specified before returning.
waitForDelay(startTime, delay, mockContext);
// Return a 404 code : not found.
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
// Return a 400 code : bad request.
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@RequestMapping(value = "/{service}/{version}/{resource}/{resourceId}", method = RequestMethod.DELETE)
public ResponseEntity deleteResource(
@PathVariable("service") String serviceName,
@PathVariable("version") String version,
@PathVariable("resource") String resource,
@PathVariable("resourceId") String resourceId,
@RequestParam(value="delay", required=false) Long delay
) {
log.debug("Update resource '{}:{}' for service '{}-{}'", resource, resourceId, serviceName, version);
long startTime = System.currentTimeMillis();
serviceName = sanitizeServiceName(serviceName);
MockContext mockContext = getMockContext(serviceName, version, "DELETE /" + resource + "/:id");
if (mockContext != null) {
genericResourceRepository.deleteById(resourceId);
// Wait if specified before returning.
waitForDelay(startTime, delay, mockContext);
// Return a 204 code : done and no content returned.
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
// Return a 400 code : bad request.
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
/** Sanitize the service name (check encoding and so on...) */
private String sanitizeServiceName(String serviceName) {
// If serviceName was encoded with '+' instead of '%20', replace them.
if (serviceName.contains("+")) {
return serviceName.replace('+', ' ');
}
return serviceName;
}
/** Retrieve a MockContext corresponding to operation on service. Null if not found or not valid. */
private MockContext getMockContext(String serviceName, String version, String operationName) {
Service service = serviceRepository.findByNameAndVersion(serviceName, version);
if (service != null && ServiceType.GENERIC_REST.equals(service.getType())) {
for (Operation operation : service.getOperations()) {
if (operationName.equals(operation.getName())) {
return new MockContext(service, operation);
}
}
}
return null;
}
private String transformToResourceJSON(GenericResource genericResource) {
Document document = genericResource.getPayload();
document.append(ID_FIELD, genericResource.getId());
return document.toJson();
}
private String formatToJSONArray(List resources) {
StringBuilder builder = new StringBuilder("[");
for (int i=0; i © 2015 - 2025 Weber Informatics LLC | Privacy Policy