use of io.github.microcks.domain.Operation in project microcks by microcks.
the class SoapController method execute.
@RequestMapping(value = "/{service}/{version}/**", method = RequestMethod.POST)
public ResponseEntity<?> execute(@PathVariable("service") String serviceName, @PathVariable("version") String version, @RequestParam(value = "validate", required = false) Boolean validate, @RequestParam(value = "delay", required = false) Long delay, @RequestBody String body, HttpServletRequest request) {
log.info("Servicing mock response for service [{}, {}]", serviceName, version);
log.debug("Request body: " + body);
long startTime = System.currentTimeMillis();
// If serviceName was encoded with '+' instead of '%20', replace them.
if (serviceName.contains("+")) {
serviceName = serviceName.replace('+', ' ');
}
log.info("Service name: " + serviceName);
// Retrieve service and correct operation.
Service service = serviceRepository.findByNameAndVersion(serviceName, version);
Operation rOperation = null;
// Enhancement : retrieve SOAPAction from request headers
String action = extractSoapAction(request);
log.debug("Extracted SOAP action from headers: {}", action);
if (action != null && action.length() > 0) {
for (Operation operation : service.getOperations()) {
if (action.equals(operation.getAction())) {
rOperation = operation;
log.info("Found valid operation {}", rOperation.getName());
break;
}
}
}
// Enhancement : if not found, try getting operation from soap:body directly!
if (rOperation == null) {
String operationName = extractOperationName(body);
log.debug("Extracted operation name from payload: {}", operationName);
if (operationName != null) {
for (Operation operation : service.getOperations()) {
if (operationName.equals(operation.getInputName()) || operationName.equals(operation.getName())) {
rOperation = operation;
log.info("Found valid operation {}", rOperation.getName());
break;
}
}
}
}
// Now processing the request and send a response.
if (rOperation != null) {
log.debug("Found a valid operation with rules: {}", rOperation.getDispatcherRules());
if (validate != null && validate) {
log.debug("Soap message validation is turned on, validating...");
try {
List<XmlError> errors = SoapMessageValidator.validateSoapMessage(rOperation.getInputName(), service.getXmlNS(), body, resourceUrl + UriUtils.encodePath(service.getName() + "-" + version, "UTF-8") + ".wsdl", true);
log.debug("SoapBody validation errors: " + errors.size());
// Return a 400 http code with errors.
if (errors != null && errors.size() > 0) {
return new ResponseEntity<Object>(errors, HttpStatus.BAD_REQUEST);
}
} catch (Exception e) {
log.error("Error during Soap validation", e);
return new ResponseEntity<Object>("Error during Soap validation: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
// We must find dispatcher and its rules. Default to operation ones but
// if we have a Fallback this is the one who is holding the first pass rules.
String dispatcher = rOperation.getDispatcher();
String dispatcherRules = rOperation.getDispatcherRules();
FallbackSpecification fallback = MockControllerCommons.getFallbackIfAny(rOperation);
if (fallback != null) {
dispatcher = fallback.getDispatcher();
dispatcherRules = fallback.getDispatcherRules();
}
Response response = null;
String dispatchCriteria = null;
// Depending on dispatcher, evaluate request with rules.
if (DispatchStyles.QUERY_MATCH.equals(dispatcher)) {
dispatchCriteria = getDispatchCriteriaFromXPathEval(dispatcherRules, body);
} else if (DispatchStyles.SCRIPT.equals(dispatcher)) {
dispatchCriteria = getDispatchCriteriaFromScriptEval(dispatcherRules, body, request);
}
log.debug("Dispatch criteria for finding response is {}", dispatchCriteria);
List<Response> responses = responseRepository.findByOperationIdAndDispatchCriteria(IdBuilder.buildOperationId(service, rOperation), dispatchCriteria);
if (responses.isEmpty() && fallback != null) {
// If we've found nothing and got a fallback, that's the moment!
responses = responseRepository.findByOperationIdAndName(IdBuilder.buildOperationId(service, rOperation), fallback.getFallback());
}
if (!responses.isEmpty()) {
response = responses.get(0);
}
// Set Content-Type to "text/xml".
HttpHeaders responseHeaders = new HttpHeaders();
// Check to see if we are processing a SOAP 1.2 request
if (request.getContentType().startsWith("application/soap+xml")) {
// we are; set Content-Type to "application/soap+xml"
responseHeaders.setContentType(MediaType.valueOf("application/soap+xml;charset=UTF-8"));
} else {
// Set Content-Type to "text/xml".
responseHeaders.setContentType(MediaType.valueOf("text/xml;charset=UTF-8"));
}
// Render response content before waiting and returning.
String responseContent = MockControllerCommons.renderResponseContent(body, null, request, response);
// Setting delay to default one if not set.
if (delay == null && rOperation.getDefaultDelay() != null) {
delay = rOperation.getDefaultDelay();
}
MockControllerCommons.waitForDelay(startTime, delay);
// Publish an invocation event before returning if enabled.
if (enableInvocationStats) {
MockControllerCommons.publishMockInvocation(applicationContext, this, service, response, startTime);
}
if (response.isFault()) {
return new ResponseEntity<Object>(responseContent, responseHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<Object>(responseContent, responseHeaders, HttpStatus.OK);
}
log.debug("No valid operation found by Microcks...");
return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
}
use of io.github.microcks.domain.Operation in project microcks by microcks.
the class ServiceService method createGenericResourceService.
/**
* Create a new Service concerning a GenericResource for dynamic mocking.
* @param name The name of the new Service to create
* @param version The version of the new Service to create
* @param resource The resource that will be exposed as CRUD operations for this service
* @return The newly created Service object
* @throws EntityAlreadyExistsException if a Service with same name and version is already present in store
*/
public Service createGenericResourceService(String name, String version, String resource) throws EntityAlreadyExistsException {
log.info("Creating a new Service '{}-{}' for generic resource {}", name, version, resource);
// Check if corresponding Service already exists.
Service existingService = serviceRepository.findByNameAndVersion(name, version);
if (existingService != null) {
log.warn("A Service '{}-{}' is already existing. Throwing an Exception", name, version);
throw new EntityAlreadyExistsException(String.format("Service '%s-%s' is already present in store", name, version));
}
// Create new service with GENERIC_REST type.
Service service = new Service();
service.setName(name);
service.setVersion(version);
service.setType(ServiceType.GENERIC_REST);
service.setMetadata(new Metadata());
// Now create basic crud operations for the resource.
Operation createOp = new Operation();
createOp.setName("POST /" + resource);
createOp.setMethod("POST");
service.addOperation(createOp);
Operation getOp = new Operation();
getOp.setName("GET /" + resource + "/:id");
getOp.setMethod("GET");
getOp.setDispatcher(DispatchStyles.URI_PARTS);
getOp.setDispatcherRules("id");
service.addOperation(getOp);
Operation updateOp = new Operation();
updateOp.setName("PUT /" + resource + "/:id");
updateOp.setMethod("PUT");
updateOp.setDispatcher(DispatchStyles.URI_PARTS);
updateOp.setDispatcherRules("id");
service.addOperation(updateOp);
Operation listOp = new Operation();
listOp.setName("GET /" + resource);
listOp.setMethod("GET");
service.addOperation(listOp);
Operation delOp = new Operation();
delOp.setName("DELETE /" + resource + "/:id");
delOp.setMethod("DELETE");
delOp.setDispatcher(DispatchStyles.URI_PARTS);
delOp.setDispatcherRules("id");
service.addOperation(delOp);
serviceRepository.save(service);
log.info("Having create Service '{}' for generic resource {}", service.getId(), resource);
return service;
}
use of io.github.microcks.domain.Operation in project microcks by microcks.
the class ServiceService method importServiceDefinition.
/**
* Import definitions of services and bounded resources and messages into Microcks
* repository. This uses a MockRepositoryImporter under hood.
* @param repositoryFile The File for mock repository.
* @param referenceResolver The Resolver to be used during import (may be null).
* @param artifactInfo The essential information on Artifact to import.
* @return The list of imported Services
* @throws MockRepositoryImportException if something goes wrong (URL not reachable nor readable, etc...)
*/
public List<Service> importServiceDefinition(File repositoryFile, ReferenceResolver referenceResolver, ArtifactInfo artifactInfo) throws MockRepositoryImportException {
// Retrieve the correct importer based on file path.
MockRepositoryImporter importer = null;
try {
importer = MockRepositoryImporterFactory.getMockRepositoryImporter(repositoryFile, referenceResolver);
} catch (IOException ioe) {
log.error("Exception while accessing file " + repositoryFile.getPath(), ioe);
throw new MockRepositoryImportException(ioe.getMessage(), ioe);
}
Service reference = null;
boolean serviceUpdate = false;
List<Service> services = importer.getServiceDefinitions();
for (Service service : services) {
Service existingService = serviceRepository.findByNameAndVersion(service.getName(), service.getVersion());
log.debug("Service [{}, {}] exists ? {}", service.getName(), service.getVersion(), existingService != null);
// If it's the main artifact: retrieve previous id and props if update, save anyway.
if (artifactInfo.isMainArtifact()) {
if (existingService != null) {
// Retrieve its previous identifier and metadatas
// (backup metadata that may have been imported with extensions).
Metadata backup = service.getMetadata();
service.setId(existingService.getId());
service.setMetadata(existingService.getMetadata());
// If there was metadata found through extensions, overwrite historical ones.
if (backup != null) {
existingService.getMetadata().setLabels(backup.getLabels());
existingService.getMetadata().setAnnotations(backup.getAnnotations());
}
// Keep its overriden operation properties.
copyOverridenOperations(existingService, service);
serviceUpdate = true;
}
if (service.getMetadata() == null) {
service.setMetadata(new Metadata());
}
// For services of type EVENT, we should put default values on frequency and bindings.
if (service.getType().equals(ServiceType.EVENT)) {
manageEventServiceDefaults(service);
}
service.getMetadata().objectUpdated();
service.setSourceArtifact(artifactInfo.getArtifactName());
service = serviceRepository.save(service);
// We're dealing with main artifact so reference is saved or updated one.
reference = service;
} else {
// It's a secondary artifact just for messages or metadata. We'll have problems if not having an existing service...
if (existingService == null) {
log.warn("Trying to import {} as a secondary artifact but there's no existing [{}, {}] Service. Just skipping.", artifactInfo.getArtifactName(), service.getName(), service.getVersion());
break;
}
// update the existing service with them.
if (service.getMetadata() != null) {
existingService.getMetadata().setLabels(service.getMetadata().getLabels());
existingService.getMetadata().setAnnotations(service.getMetadata().getAnnotations());
}
for (Operation operation : service.getOperations()) {
Operation existingOp = existingService.getOperations().stream().filter(op -> op.getName().equals(operation.getName())).findFirst().orElse(null);
if (existingOp != null) {
if (operation.getDefaultDelay() != null) {
existingOp.setDefaultDelay(operation.getDefaultDelay());
}
if (operation.getDispatcher() != null) {
existingOp.setDispatcher(operation.getDispatcher());
}
if (operation.getDispatcherRules() != null) {
existingOp.setDispatcherRules(operation.getDispatcherRules());
}
}
}
// We're dealing with secondary artifact so reference is the pre-existing one.
// Moreover, we should replace current imported service (unbound/unsaved)
// by reference in the results list.
reference = existingService;
services.remove(service);
services.add(reference);
}
// Remove resources previously attached to service.
List<Resource> existingResources = resourceRepository.findByServiceIdAndSourceArtifact(reference.getId(), artifactInfo.getArtifactName());
if (existingResources != null && existingResources.size() > 0) {
resourceRepository.deleteAll(existingResources);
}
// Save new resources.
List<Resource> resources = importer.getResourceDefinitions(service);
for (Resource resource : resources) {
resource.setServiceId(reference.getId());
resource.setSourceArtifact(artifactInfo.getArtifactName());
}
resourceRepository.saveAll(resources);
for (Operation operation : reference.getOperations()) {
String operationId = IdBuilder.buildOperationId(reference, operation);
// Remove messages previously attached to service.
requestRepository.deleteAll(requestRepository.findByOperationIdAndSourceArtifact(operationId, artifactInfo.getArtifactName()));
responseRepository.deleteAll(responseRepository.findByOperationIdAndSourceArtifact(operationId, artifactInfo.getArtifactName()));
eventMessageRepository.deleteAll(eventMessageRepository.findByOperationIdAndSourceArtifact(operationId, artifactInfo.getArtifactName()));
List<Exchange> exchanges = importer.getMessageDefinitions(service, operation);
for (Exchange exchange : exchanges) {
if (exchange instanceof RequestResponsePair) {
RequestResponsePair pair = (RequestResponsePair) exchange;
// Associate request and response with operation and artifact.
pair.getRequest().setOperationId(operationId);
pair.getResponse().setOperationId(operationId);
pair.getRequest().setSourceArtifact(artifactInfo.getArtifactName());
pair.getResponse().setSourceArtifact(artifactInfo.getArtifactName());
// Save response and associate request with response before saving it.
responseRepository.save(pair.getResponse());
pair.getRequest().setResponseId(pair.getResponse().getId());
requestRepository.save(pair.getRequest());
} else if (exchange instanceof UnidirectionalEvent) {
UnidirectionalEvent event = (UnidirectionalEvent) exchange;
// Associate event message with operation and artifact before saving it..
event.getEventMessage().setOperationId(operationId);
event.getEventMessage().setSourceArtifact(artifactInfo.getArtifactName());
eventMessageRepository.save(event.getEventMessage());
}
}
}
// When extracting message information, we may have modified Operation because discovered new resource paths
// depending on variable URI parts. As a consequence, we got to update Service in repository.
serviceRepository.save(reference);
// Publish a Service update event before returning.
publishServiceChangeEvent(reference, serviceUpdate ? ChangeType.UPDATED : ChangeType.CREATED);
}
log.info("Having imported {} services definitions into repository", services.size());
return services;
}
use of io.github.microcks.domain.Operation in project microcks by microcks.
the class ServiceService method deleteService.
/**
* Remove a Service and its bound documents using the service id.
* @param id The identifier of service to remove.
* @param userInfo The current user information to check if authorized to delete
* @return True if service has been found and updated, false otherwise.
*/
public Boolean deleteService(String id, UserInfo userInfo) {
// Get service to remove.
Service service = serviceRepository.findById(id).orElse(null);
if (authorizationChecker.hasRole(userInfo, AuthorizationChecker.ROLE_ADMIN) || authorizationChecker.hasRoleForService(userInfo, AuthorizationChecker.ROLE_MANAGER, service)) {
// Delete all resources first.
resourceRepository.deleteAll(resourceRepository.findByServiceId(id));
// Delete all requests and responses bound to service operation.
if (service != null) {
for (Operation operation : service.getOperations()) {
String operationId = IdBuilder.buildOperationId(service, operation);
requestRepository.deleteAll(requestRepository.findByOperationId(operationId));
responseRepository.deleteAll(responseRepository.findByOperationId(operationId));
}
}
// Delete all tests related to service.
testResultRepository.deleteAll(testResultRepository.findByServiceId(id));
// Finally delete service and publish event.
serviceRepository.delete(service);
publishServiceChangeEvent(service, ChangeType.DELETED);
log.info("Service [{}] has been fully deleted", id);
return true;
}
return false;
}
use of io.github.microcks.domain.Operation in project microcks by microcks.
the class ServiceService method updateOperation.
/**
* Update the default delay of a Service operation
* @param id The identifier of service to update operation for
* @param operationName The name of operation to update delay for
* @param dispatcher The dispatcher to use for this operation
* @param dispatcherRules The dispatcher rules to use for this operation
* @param delay The new delay value for operation
* @param constraints Constraints for this operation parameters
* @param userInfo The current user information to check if authorized to do the update
* @return True if operation has been found and updated, false otherwise.
*/
public Boolean updateOperation(String id, String operationName, String dispatcher, String dispatcherRules, Long delay, List<ParameterConstraint> constraints, UserInfo userInfo) {
Service service = serviceRepository.findById(id).orElse(null);
log.debug("Is user allowed? " + authorizationChecker.hasRoleForService(userInfo, AuthorizationChecker.ROLE_MANAGER, service));
if (service != null && authorizationChecker.hasRoleForService(userInfo, AuthorizationChecker.ROLE_MANAGER, service)) {
for (Operation operation : service.getOperations()) {
if (operation.getName().equals(operationName)) {
operation.setDispatcher(dispatcher);
operation.setDispatcherRules(dispatcherRules);
operation.setParameterConstraints(constraints);
operation.setDefaultDelay(delay);
operation.setOverride(true);
serviceRepository.save(service);
// Publish a Service update event before returning.
publishServiceChangeEvent(service, ChangeType.UPDATED);
return true;
}
}
}
return false;
}
Aggregations