All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.microcks.web.DynamicMockRestController Maven / Gradle / Ivy

There is a newer version: 1.11.0
Show newest version
/*
 * Licensed to Laurent Broudoux (the "Author") under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. Author licenses this
 * file to you 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 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.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @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;

   @RequestMapping(value = "/{service}/{version}/{resource}", method = RequestMethod.POST)
   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)
   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
   ) {
      log.debug("Find resources '{}' for service '{}-{}'", resource, serviceName, version);
      long startTime = System.currentTimeMillis();

      serviceName = sanitizeServiceName(serviceName);

      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);
         }

         // Transform and collect resources.
         List resources = genericResources.stream()
               .map(genericResource -> transformToResourceJSON(genericResource))
               .collect(Collectors.toList());

         // Wait if specified before returning.
         waitForDelay(startTime, delay, mockContext);
         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)
   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
   ) {
      log.debug("Get resource '{}:{}' for service '{}-{}'", resource, resourceId, serviceName, version);
      long startTime = System.currentTimeMillis();

      serviceName = sanitizeServiceName(serviceName);

      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) {
            // Return the resource as well as a 200 code.
            return new ResponseEntity<>(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)
   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 -1) {
         log.debug("Mock delay is turned on, waiting if necessary...");
         long duration = System.currentTimeMillis() - since;
         if (duration < delay) {
            Object semaphore = new Object();
            synchronized (semaphore) {
               try {
                  semaphore.wait(delay - duration);
               } catch (Exception e) {
                  log.debug("Delay semaphore was interrupted");
               }
            }
         }
         log.debug("Delay now expired, releasing response !");
      }

      // Publish an invocation event before returning.
      MockInvocationEvent event = new MockInvocationEvent(this, mockContext.service.getName(),
            mockContext.service.getVersion(),
            "DynamicMockRestController",
            new Date(since), since - System.currentTimeMillis());
      applicationContext.publishEvent(event);
      log.debug("Mock invocation event has been published");
   }

   private class MockContext {
      public Service service;
      public Operation operation;

      public MockContext(Service service, Operation operation) {
         this.service = service;
         this.operation = operation;
      }
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy