Search in sources :

Example 16 with Operation

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);
}
Also used : HttpHeaders(org.springframework.http.HttpHeaders) Service(io.github.microcks.domain.Service) XmlError(org.apache.xmlbeans.XmlError) Operation(io.github.microcks.domain.Operation) Response(io.github.microcks.domain.Response) ResponseEntity(org.springframework.http.ResponseEntity) FallbackSpecification(io.github.microcks.util.dispatcher.FallbackSpecification) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 17 with Operation

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;
}
Also used : EntityAlreadyExistsException(io.github.microcks.util.EntityAlreadyExistsException) Metadata(io.github.microcks.domain.Metadata) Service(io.github.microcks.domain.Service) Operation(io.github.microcks.domain.Operation)

Example 18 with Operation

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;
}
Also used : RequestResponsePair(io.github.microcks.domain.RequestResponsePair) Metadata(io.github.microcks.domain.Metadata) Resource(io.github.microcks.domain.Resource) UnidirectionalEvent(io.github.microcks.domain.UnidirectionalEvent) Service(io.github.microcks.domain.Service) MockRepositoryImporter(io.github.microcks.util.MockRepositoryImporter) IOException(java.io.IOException) Operation(io.github.microcks.domain.Operation) MockRepositoryImportException(io.github.microcks.util.MockRepositoryImportException) Exchange(io.github.microcks.domain.Exchange)

Example 19 with Operation

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;
}
Also used : Service(io.github.microcks.domain.Service) Operation(io.github.microcks.domain.Operation)

Example 20 with Operation

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;
}
Also used : Service(io.github.microcks.domain.Service) Operation(io.github.microcks.domain.Operation)

Aggregations

Operation (io.github.microcks.domain.Operation)26 Service (io.github.microcks.domain.Service)14 JsonNode (com.fasterxml.jackson.databind.JsonNode)7 IOException (java.io.IOException)7 ArrayList (java.util.ArrayList)7 MockRepositoryImportException (io.github.microcks.util.MockRepositoryImportException)6 Resource (io.github.microcks.domain.Resource)5 UnidirectionalEvent (io.github.microcks.domain.UnidirectionalEvent)5 Exchange (io.github.microcks.domain.Exchange)4 RequestResponsePair (io.github.microcks.domain.RequestResponsePair)4 Response (io.github.microcks.domain.Response)4 ServiceType (io.github.microcks.domain.ServiceType)4 Test (org.junit.Test)4 Header (io.github.microcks.domain.Header)3 ServiceView (io.github.microcks.domain.ServiceView)3 List (java.util.List)3 Collectors (java.util.stream.Collectors)3 FieldDefinition (graphql.language.FieldDefinition)2 InputValueDefinition (graphql.language.InputValueDefinition)2 Metadata (io.github.microcks.domain.Metadata)2