Search in sources :

Example 1 with Response

use of io.github.microcks.domain.Response in project microcks by microcks.

the class RestController method execute.

@RequestMapping(value = "/{service}/{version}/**", method = { RequestMethod.HEAD, RequestMethod.OPTIONS, RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.PATCH, RequestMethod.DELETE })
public ResponseEntity<?> execute(@PathVariable("service") String serviceName, @PathVariable("version") String version, @RequestParam(value = "delay", required = false) Long delay, @RequestBody(required = false) String body, HttpServletRequest request) {
    log.info("Servicing mock response for service [{}, {}] on uri {} with verb {}", serviceName, version, request.getRequestURI(), request.getMethod());
    log.debug("Request body: {}", body);
    long startTime = System.currentTimeMillis();
    // Extract resourcePath for matching with correct operation.
    String requestURI = request.getRequestURI();
    String serviceAndVersion = null;
    String resourcePath = null;
    // Build the encoded URI fragment to retrieve simple resourcePath.
    serviceAndVersion = "/" + UriUtils.encodeFragment(serviceName, "UTF-8") + "/" + version;
    resourcePath = requestURI.substring(requestURI.indexOf(serviceAndVersion) + serviceAndVersion.length());
    // resourcePath = UriUtils.decode(resourcePath, "UTF-8");
    log.debug("Found resourcePath: {}", resourcePath);
    // If serviceName was encoded with '+' instead of '%20', remove them.
    if (serviceName.contains("+")) {
        serviceName = serviceName.replace('+', ' ');
    }
    // If resourcePath was encoded with '+' instead of '%20', replace them .
    if (resourcePath.contains("+")) {
        resourcePath = resourcePath.replace("+", "%20");
    }
    Service service = serviceRepository.findByNameAndVersion(serviceName, version);
    Operation rOperation = null;
    for (Operation operation : service.getOperations()) {
        // Select operation based onto Http verb (GET, POST, PUT, etc ...)
        if (operation.getMethod().equals(request.getMethod().toUpperCase())) {
            // ... then check is we have a matching resource path.
            if (operation.getResourcePaths() != null && operation.getResourcePaths().contains(resourcePath)) {
                rOperation = operation;
                break;
            }
        }
    }
    // using a Fallback dispatcher. Try again, just considering the verb and path pattern of operation.
    if (rOperation == null) {
        for (Operation operation : service.getOperations()) {
            // Select operation based onto Http verb (GET, POST, PUT, etc ...)
            if (operation.getMethod().equals(request.getMethod().toUpperCase())) {
                // ... then check is current resource path matches operation path pattern.
                if (operation.getResourcePaths() != null) {
                    // Produce a matching regexp removing {part} and :part from pattern.
                    String operationPattern = getURIPattern(operation.getName());
                    operationPattern = operationPattern.replaceAll("\\{.+\\}", "(.)+");
                    operationPattern = operationPattern.replaceAll("(/:[^:^/]+)", "\\/(.+)");
                    if (resourcePath.matches(operationPattern)) {
                        rOperation = operation;
                        break;
                    }
                }
            }
        }
    }
    if (rOperation != null) {
        log.debug("Found a valid operation {} with rules: {}", rOperation.getName(), rOperation.getDispatcherRules());
        String violationMsg = validateParameterConstraintsIfAny(rOperation, request);
        if (violationMsg != null) {
            return new ResponseEntity<Object>(violationMsg + ". Check parameter constraints.", 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();
        }
        // 
        String dispatchCriteria = computeDispatchCriteria(dispatcher, dispatcherRules, getURIPattern(rOperation.getName()), UriUtils.decode(resourcePath, "UTF-8"), request, body);
        log.debug("Dispatch criteria for finding response is {}", dispatchCriteria);
        Response response = null;
        // Filter depending on requested media type.
        List<Response> responses = responseRepository.findByOperationIdAndDispatchCriteria(IdBuilder.buildOperationId(service, rOperation), dispatchCriteria);
        response = getResponseByMediaType(responses, request);
        if (response == null) {
            // When using the SCRIPT or JSON_BODY dispatchers, return of evaluation may be the name of response.
            responses = responseRepository.findByOperationIdAndName(IdBuilder.buildOperationId(service, rOperation), dispatchCriteria);
            response = getResponseByMediaType(responses, request);
        }
        if (response == null && fallback != null) {
            // If we've found nothing and got a fallback, that's the moment!
            responses = responseRepository.findByOperationIdAndName(IdBuilder.buildOperationId(service, rOperation), fallback.getFallback());
            response = getResponseByMediaType(responses, request);
        }
        if (response == null) {
            // In case no response found (because dispatcher is null for example), just get one for the operation.
            // This will allow also OPTIONS operations (like pre-flight requests) with no dispatch criteria to work.
            log.debug("No responses found so far, tempting with just bare operationId...");
            responses = responseRepository.findByOperationId(IdBuilder.buildOperationId(service, rOperation));
            if (!responses.isEmpty()) {
                response = getResponseByMediaType(responses, request);
            }
        }
        if (response != null) {
            HttpStatus status = (response.getStatus() != null ? HttpStatus.valueOf(Integer.parseInt(response.getStatus())) : HttpStatus.OK);
            // Deal with specific headers (content-type and redirect directive).
            HttpHeaders responseHeaders = new HttpHeaders();
            if (response.getMediaType() != null) {
                responseHeaders.setContentType(MediaType.valueOf(response.getMediaType() + ";charset=UTF-8"));
            }
            // Deal with headers from parameter constraints if any?
            recopyHeadersFromParameterConstraints(rOperation, request, responseHeaders);
            // Adding other generic headers (caching directives and so on...)
            if (response.getHeaders() != null) {
                for (Header header : response.getHeaders()) {
                    if ("Location".equals(header.getName())) {
                        // We should process location in order to make relative URI specified an absolute one from
                        // the client perspective.
                        String location = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/rest" + serviceAndVersion + header.getValues().iterator().next();
                        responseHeaders.add(header.getName(), location);
                    } else {
                        if (!HttpHeaders.TRANSFER_ENCODING.equalsIgnoreCase(header.getName())) {
                            responseHeaders.put(header.getName(), new ArrayList<>(header.getValues()));
                        }
                    }
                }
            }
            // Render response content before waiting and returning.
            String responseContent = MockControllerCommons.renderResponseContent(body, resourcePath, 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);
            }
            return new ResponseEntity<Object>(responseContent, responseHeaders, status);
        }
        return new ResponseEntity<Object>(HttpStatus.BAD_REQUEST);
    } else // Handle OPTIONS request if CORS policy is enabled.
    if (enableCorsPolicy && "OPTIONS".equals(request.getMethod().toUpperCase())) {
        log.debug("No valid operation found but Microcks configured to apply CORS policy");
        return handleCorsRequest(request);
    }
    log.debug("No valid operation found and Microcks configured to not apply CORS policy...");
    return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
}
Also used : Response(io.github.microcks.domain.Response) HttpHeaders(org.springframework.http.HttpHeaders) ResponseEntity(org.springframework.http.ResponseEntity) Header(io.github.microcks.domain.Header) FallbackSpecification(io.github.microcks.util.dispatcher.FallbackSpecification) HttpStatus(org.springframework.http.HttpStatus) Service(io.github.microcks.domain.Service) Operation(io.github.microcks.domain.Operation) RequestMapping(org.springframework.web.bind.annotation.RequestMapping)

Example 2 with Response

use of io.github.microcks.domain.Response in project microcks by microcks.

the class ServiceServiceTest method testImportServiceDefinition.

@Test
public void testImportServiceDefinition() {
    List<Service> services = null;
    try {
        File artifactFile = new File("target/test-classes/io/github/microcks/service/weather-forecast-openapi.yaml");
        services = service.importServiceDefinition(artifactFile, null, new ArtifactInfo("weather-forecast-openapi.yaml", true));
    } catch (MockRepositoryImportException mrie) {
        mrie.printStackTrace();
        fail("No MockRepositoryImportException should have be thrown");
    }
    assertNotNull(services);
    assertEquals(1, services.size());
    // Inspect Service own attributes.
    Service importedSvc = services.get(0);
    assertEquals("WeatherForecast API", importedSvc.getName());
    assertEquals("1.0.0", importedSvc.getVersion());
    assertEquals("weather-forecast-openapi.yaml", importedSvc.getSourceArtifact());
    assertNotNull(importedSvc.getMetadata());
    assertEquals(1, importedSvc.getOperations().size());
    assertEquals("GET /forecast/{region}", importedSvc.getOperations().get(0).getName());
    assertEquals(5, importedSvc.getOperations().get(0).getResourcePaths().size());
    // Inspect and check resources.
    List<Resource> resources = resourceRepository.findByServiceId(importedSvc.getId());
    assertEquals(1, resources.size());
    Resource resource = resources.get(0);
    assertEquals("WeatherForecast API-1.0.0.yaml", resource.getName());
    assertEquals("weather-forecast-openapi.yaml", resource.getSourceArtifact());
    // Inspect and check requests.
    List<Request> requests = requestRepository.findByOperationId(IdBuilder.buildOperationId(importedSvc, importedSvc.getOperations().get(0)));
    assertEquals(5, requests.size());
    for (Request request : requests) {
        assertEquals("weather-forecast-openapi.yaml", request.getSourceArtifact());
    }
    // Inspect and check responses.
    List<Response> responses = responseRepository.findByOperationId(IdBuilder.buildOperationId(importedSvc, importedSvc.getOperations().get(0)));
    assertEquals(5, requests.size());
    for (Response response : responses) {
        assertEquals("weather-forecast-openapi.yaml", response.getSourceArtifact());
    }
}
Also used : Response(io.github.microcks.domain.Response) Resource(io.github.microcks.domain.Resource) Request(io.github.microcks.domain.Request) Service(io.github.microcks.domain.Service) File(java.io.File) MockRepositoryImportException(io.github.microcks.util.MockRepositoryImportException) Test(org.junit.Test)

Example 3 with Response

use of io.github.microcks.domain.Response in project microcks by microcks.

the class ServiceServiceTest method testImportServiceDefinitionMainGraphQLAndSecondaryPostman.

@Test
public void testImportServiceDefinitionMainGraphQLAndSecondaryPostman() {
    List<Service> services = null;
    try {
        File artifactFile = new File("target/test-classes/io/github/microcks/util/graphql/films.graphql");
        services = service.importServiceDefinition(artifactFile, null, new ArtifactInfo("films.graphql", true));
    } catch (MockRepositoryImportException mrie) {
        mrie.printStackTrace();
        fail("No MockRepositoryImportException should have be thrown");
    }
    assertNotNull(services);
    assertEquals(1, services.size());
    // Inspect Service own attributes.
    Service importedSvc = services.get(0);
    assertEquals("Movie Graph API", importedSvc.getName());
    assertEquals("1.0", importedSvc.getVersion());
    assertEquals("films.graphql", importedSvc.getSourceArtifact());
    assertNotNull(importedSvc.getMetadata());
    assertEquals(4, importedSvc.getOperations().size());
    // Inspect and check requests.
    List<Request> requests = requestRepository.findByOperationId(IdBuilder.buildOperationId(importedSvc, importedSvc.getOperations().get(0)));
    assertEquals(0, requests.size());
    // Inspect and check responses.
    List<Response> responses = responseRepository.findByOperationId(IdBuilder.buildOperationId(importedSvc, importedSvc.getOperations().get(0)));
    assertEquals(0, responses.size());
    try {
        File artifactFile = new File("target/test-classes/io/github/microcks/util/graphql/films-postman.json");
        services = service.importServiceDefinition(artifactFile, null, new ArtifactInfo("films-postman.json", false));
    } catch (MockRepositoryImportException mrie) {
        mrie.printStackTrace();
        fail("No MockRepositoryImportException should have be thrown");
    }
    // Inspect Service own attributes.
    importedSvc = services.get(0);
    assertEquals("Movie Graph API", importedSvc.getName());
    assertEquals("1.0", importedSvc.getVersion());
    assertEquals("films.graphql", importedSvc.getSourceArtifact());
    assertNotNull(importedSvc.getMetadata());
    assertEquals(4, importedSvc.getOperations().size());
    // Inspect and check requests.
    requests = requestRepository.findByOperationId(IdBuilder.buildOperationId(importedSvc, importedSvc.getOperations().get(0)));
    assertEquals(1, requests.size());
    for (Request request : requests) {
        assertEquals("films-postman.json", request.getSourceArtifact());
    }
    // Inspect and check responses.
    responses = responseRepository.findByOperationId(IdBuilder.buildOperationId(importedSvc, importedSvc.getOperations().get(0)));
    assertEquals(1, requests.size());
    for (Response response : responses) {
        assertEquals("films-postman.json", response.getSourceArtifact());
    }
}
Also used : Response(io.github.microcks.domain.Response) Request(io.github.microcks.domain.Request) Service(io.github.microcks.domain.Service) File(java.io.File) MockRepositoryImportException(io.github.microcks.util.MockRepositoryImportException) Test(org.junit.Test)

Example 4 with Response

use of io.github.microcks.domain.Response in project microcks by microcks.

the class PostmanCollectionImporter method getMessageDefinitionsV2.

private Map<Request, Response> getMessageDefinitionsV2(String folderName, JsonNode itemNode, Operation operation) {
    log.debug("Extracting message definitions in folder " + folderName);
    Map<Request, Response> result = new HashMap<Request, Response>();
    String itemNodeName = itemNode.path("name").asText();
    if (!itemNode.has("request")) {
        // Item is simply a folder that may contain some other folders recursively.
        Iterator<JsonNode> items = itemNode.path("item").elements();
        while (items.hasNext()) {
            JsonNode item = items.next();
            result.putAll(getMessageDefinitionsV2(folderName + "/" + itemNodeName, item, operation));
        }
    } else {
        // Item is here an operation description.
        String operationName = buildOperationName(itemNode, folderName);
        // Select item based onto operation name.
        if (areOperationsEquivalent(operation.getName(), operationName)) {
            Iterator<JsonNode> responses = itemNode.path("response").elements();
            while (responses.hasNext()) {
                JsonNode responseNode = responses.next();
                JsonNode requestNode = responseNode.path("originalRequest");
                // Build dispatchCriteria and complete operation resourcePaths.
                String dispatchCriteria = null;
                String requestUrl = requestNode.path("url").path("raw").asText();
                if (DispatchStyles.URI_PARAMS.equals(operation.getDispatcher())) {
                    dispatchCriteria = DispatchCriteriaHelper.extractFromURIParams(operation.getDispatcherRules(), requestUrl);
                    // We only need the pattern here.
                    operation.addResourcePath(extractResourcePath(requestUrl, null));
                } else if (DispatchStyles.URI_PARTS.equals(operation.getDispatcher())) {
                    Map<String, String> parts = buildRequestParts(requestNode);
                    dispatchCriteria = DispatchCriteriaHelper.buildFromPartsMap(parts);
                    // We should complete resourcePath here.
                    String resourcePath = extractResourcePath(requestUrl, null);
                    operation.addResourcePath(URIBuilder.buildURIFromPattern(resourcePath, parts));
                } else if (DispatchStyles.URI_ELEMENTS.equals(operation.getDispatcher())) {
                    Map<String, String> parts = buildRequestParts(requestNode);
                    dispatchCriteria = DispatchCriteriaHelper.buildFromPartsMap(parts);
                    dispatchCriteria += DispatchCriteriaHelper.extractFromURIParams(operation.getDispatcherRules(), requestUrl);
                    // We should complete resourcePath here.
                    String resourcePath = extractResourcePath(requestUrl, null);
                    operation.addResourcePath(URIBuilder.buildURIFromPattern(resourcePath, parts));
                } else if (DispatchStyles.QUERY_ARGS.equals(operation.getDispatcher())) {
                    // This dispatcher is used for GraphQL
                    if (requestNode.path("body").has("graphql")) {
                        // We must transform JSON representing variables into a Map<String, String>
                        // before building a dispatchCriteria matching the rules.
                        String variables = requestNode.path("body").path("graphql").path("variables").asText();
                        dispatchCriteria = extractGraphQLCriteria(operation.getDispatcherRules(), variables);
                    }
                }
                Request request = buildRequest(requestNode, responseNode.path("name").asText());
                Response response = buildResponse(responseNode, dispatchCriteria);
                result.put(request, response);
            }
        }
    }
    return result;
}
Also used : Response(io.github.microcks.domain.Response) Request(io.github.microcks.domain.Request) JsonNode(com.fasterxml.jackson.databind.JsonNode)

Example 5 with Response

use of io.github.microcks.domain.Response in project microcks by microcks.

the class PostmanCollectionImporter method buildResponse.

private Response buildResponse(JsonNode responseNode, String dispatchCriteria) {
    Response response = new Response();
    response.setName(responseNode.path("name").asText());
    if (isV2Collection) {
        response.setStatus(responseNode.path("code").asText("200"));
        response.setHeaders(buildHeaders(responseNode.path("header")));
        response.setContent(responseNode.path("body").asText(""));
    } else {
        response.setStatus(responseNode.path("responseCode").path("code").asText());
        response.setHeaders(buildHeaders(responseNode.path("headers")));
        response.setContent(responseNode.path("text").asText());
    }
    if (response.getHeaders() != null) {
        for (Header header : response.getHeaders()) {
            if (header.getName().equalsIgnoreCase("Content-Type")) {
                response.setMediaType(header.getValues().toArray(new String[] {})[0]);
            }
        }
    }
    // assume it is its content-type.
    if (!isV2Collection && response.getMediaType() == null) {
        if ("json".equals(responseNode.path("language").asText())) {
            response.setMediaType("application/json");
        }
    }
    // assume it is its content-type.
    if (isV2Collection && response.getMediaType() == null) {
        if ("json".equals(responseNode.path("_postman_previewlanguage").asText())) {
            response.setMediaType("application/json");
        } else if ("xml".equals(responseNode.path("_postman_previewlanguage").asText())) {
            response.setMediaType("text/xml");
        }
    }
    response.setDispatchCriteria(dispatchCriteria);
    return response;
}
Also used : Response(io.github.microcks.domain.Response) Header(io.github.microcks.domain.Header)

Aggregations

Response (io.github.microcks.domain.Response)13 Request (io.github.microcks.domain.Request)8 Service (io.github.microcks.domain.Service)6 Header (io.github.microcks.domain.Header)5 Resource (io.github.microcks.domain.Resource)5 JsonNode (com.fasterxml.jackson.databind.JsonNode)4 Operation (io.github.microcks.domain.Operation)4 MockRepositoryImportException (io.github.microcks.util.MockRepositoryImportException)4 IOException (java.io.IOException)4 TestReturn (io.github.microcks.domain.TestReturn)3 FallbackSpecification (io.github.microcks.util.dispatcher.FallbackSpecification)3 File (java.io.File)3 ArrayList (java.util.ArrayList)3 Test (org.junit.Test)3 ClientHttpResponse (org.springframework.http.client.ClientHttpResponse)2 JsonGenerator (com.fasterxml.jackson.core.JsonGenerator)1 JsonProcessingException (com.fasterxml.jackson.core.JsonProcessingException)1 JsonWriteFeature (com.fasterxml.jackson.core.json.JsonWriteFeature)1 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)1 ObjectNode (com.fasterxml.jackson.databind.node.ObjectNode)1