diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/README.md b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/README.md index 1ae0d83c2..b5f51b0ec 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/README.md +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/README.md @@ -3,4 +3,12 @@ This module represents a concept of a light weight AWS forwarding proxy which deploys and interacts with existing Spring Boot web application deployed as AWS Lambda. -A sample is provided in `/sample` directory \ No newline at end of file + +A sample is provided in [sample](https://github.com/spring-cloud/spring-cloud-function/tree/serverless-web/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store) directory. It contain README and SAM template file to simplify the deployment. This module is identified as the only additional dependnecy to the existing web-app. + +_NOTE: Although this module is AWS specific, this dependency is protocol only (not binary), therefore there is no AWS dependnecies._ + +The aformentioned proxy is identified as AWS Lambda [handler](https://github.com/spring-cloud/spring-cloud-function/blob/serverless-web/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store/template.yml#L14) + +The main Spring Boot configuration file is identified as [MAIN_CLASS](https://github.com/spring-cloud/spring-cloud-function/blob/serverless-web/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/sample/pet-store/template.yml#L22) + diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/web/client/WebProxyUtils.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/web/client/WebProxyUtils.java new file mode 100644 index 000000000..22c6e39a6 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/main/java/org/springframework/web/client/WebProxyUtils.java @@ -0,0 +1,71 @@ +package org.springframework.web.client; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; + +import org.springframework.cloud.function.adapter.aws.web.FunctionClassUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +public class WebProxyUtils { + + public static ProxyMvc buildMvcProxy(Class... componentClasses) { + if (ObjectUtils.isEmpty(componentClasses)) { + Class startClass = FunctionClassUtils.getStartClass(); + if (startClass != null) { + componentClasses = new Class[] {startClass}; + } + else { + throw new IllegalStateException("Failed to discover component classes"); + } + } + AnnotationConfigWebApplicationContext applpicationContext = new AnnotationConfigWebApplicationContext(); + applpicationContext.register(componentClasses); + + ServletContext servletContext = new ProxyServletContext(); + ServletConfig servletConfig = new ProxyServletConfig(servletContext); + + DispatcherServlet servlet = new DispatcherServlet(applpicationContext); + try { + servlet.init(servletConfig); + return new ProxyMvc(servlet, applpicationContext); + } + catch (Exception e) { + throw new IllegalStateException("Failed to create MVC Proxy", e); + } + } + + private static class ProxyServletConfig implements ServletConfig { + + private final ServletContext servletContext; + + ProxyServletConfig(ServletContext servletContext) { + this.servletContext = servletContext; + } + + @Override + public String getServletName() { + return "serverless-proxy"; + } + + @Override + public ServletContext getServletContext() { + return this.servletContext; + } + + @Override + public Enumeration getInitParameterNames() { + return Collections.enumeration(new ArrayList()); + } + + @Override + public String getInitParameter(String name) { + return null; + } + } +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/test/java/org/springframework/cloud/function/adapter/aws/web/RequestResponseTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/test/java/org/springframework/cloud/function/adapter/aws/web/RequestResponseTests.java new file mode 100644 index 000000000..babfdc676 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws-web/src/test/java/org/springframework/cloud/function/adapter/aws/web/RequestResponseTests.java @@ -0,0 +1,78 @@ +package org.springframework.cloud.function.adapter.aws.web; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.web.client.ProxyHttpServletRequest; +import org.springframework.web.client.ProxyHttpServletResponse; +import org.springframework.web.client.ProxyMvc; +import org.springframework.web.client.WebProxyUtils; + +import com.fasterxml.jackson.core.type.ResolvedType; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class RequestResponseTests { + + private ObjectMapper mapper = new ObjectMapper(); + + private ProxyMvc mvc; + + @BeforeEach + public void before() { + this.mvc = WebProxyUtils.buildMvcProxy(PetStoreSpringAppConfig.class); + } + + @AfterEach + public void after() { + this.mvc.stop(); + } + + @Test + public void validateGetListOfPojos() throws Exception { + HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets"); + ProxyHttpServletResponse response = new ProxyHttpServletResponse(); + mvc.service(request, response); + TypeReference> tr = new TypeReference>() { + }; + List pets = mapper.readValue(response.getContentAsByteArray(), tr); + assertThat(pets.size()).isEqualTo(10); + assertThat(pets.get(0)).isInstanceOf(Pet.class); + } + + @Test + public void validateGetPojo() throws Exception { + HttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets/6e3cc370-892f-4efe-a9eb-82926ff8cc5b"); + ProxyHttpServletResponse response = new ProxyHttpServletResponse(); + mvc.service(request, response); + Pet pet = mapper.readValue(response.getContentAsByteArray(), Pet.class); + assertThat(pet).isNotNull(); + assertThat(pet.getName()).isNotEmpty(); + } + + @Test + public void validatePost() throws Exception { + ProxyHttpServletRequest request = new ProxyHttpServletRequest(null, "POST", "/pets/"); + String jsonPet = "{\n" + + " \"id\":\"1234\",\n" + + " \"breed\":\"Canish\",\n" + + " \"name\":\"Foo\",\n" + + " \"date\":\"2012-04-23T18:25:43.511Z\"\n" + + "}"; + request.setContent(jsonPet.getBytes()); + request.setContentType("application/json"); + ProxyHttpServletResponse response = new ProxyHttpServletResponse(); + mvc.service(request, response); + Pet pet = mapper.readValue(response.getContentAsByteArray(), Pet.class); + assertThat(pet).isNotNull(); + assertThat(pet.getName()).isNotEmpty(); + } + +}