Add spring-cloud-function-serverless-web module

This commit is contained in:
Oleg Zhurakousky
2023-03-06 18:43:33 +01:00
parent a9b17bd70d
commit dbf362b7fe
23 changed files with 3516 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2023-2023 the original author or authors.
*
* Licensed 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
*
* https://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 org.springframework.cloud.function.adapter.aws.web;
import java.util.Date;
public class Pet {
private String id;
private String breed;
private String name;
private Date dateOfBirth;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2023-2023 the original author or authors.
*
* Licensed 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
*
* https://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 org.springframework.cloud.function.adapter.aws.web;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class PetData {
private static List<String> breeds = new ArrayList<>();
static {
breeds.add("Afghan Hound");
breeds.add("Beagle");
breeds.add("Bernese Mountain Dog");
breeds.add("Bloodhound");
breeds.add("Dalmatian");
breeds.add("Jack Russell Terrier");
breeds.add("Norwegian Elkhound");
}
private static List<String> names = new ArrayList<>();
static {
names.add("Bailey");
names.add("Bella");
names.add("Max");
names.add("Lucy");
names.add("Charlie");
names.add("Molly");
names.add("Buddy");
names.add("Daisy");
names.add("Rocky");
names.add("Maggie");
names.add("Jake");
names.add("Sophie");
names.add("Jack");
names.add("Sadie");
names.add("Toby");
names.add("Chloe");
names.add("Cody");
names.add("Bailey");
names.add("Buster");
names.add("Lola");
names.add("Duke");
names.add("Zoe");
names.add("Cooper");
names.add("Abby");
names.add("Riley");
names.add("Ginger");
names.add("Harley");
names.add("Roxy");
names.add("Bear");
names.add("Gracie");
names.add("Tucker");
names.add("Coco");
names.add("Murphy");
names.add("Sasha");
names.add("Lucky");
names.add("Lily");
names.add("Oliver");
names.add("Angel");
names.add("Sam");
names.add("Princess");
names.add("Oscar");
names.add("Emma");
names.add("Teddy");
names.add("Annie");
names.add("Winston");
names.add("Rosie");
}
public static List<String> getBreeds() {
return breeds;
}
public static List<String> getNames() {
return names;
}
public static String getRandomBreed() {
return breeds.get(ThreadLocalRandom.current().nextInt(0, breeds.size() - 1));
}
public static String getRandomName() {
return names.get(ThreadLocalRandom.current().nextInt(0, names.size() - 1));
}
public static Date getRandomDoB() {
GregorianCalendar gc = new GregorianCalendar();
int year = ThreadLocalRandom.current().nextInt(Calendar.getInstance().get(Calendar.YEAR) - 15,
Calendar.getInstance().get(Calendar.YEAR));
gc.set(Calendar.YEAR, year);
int dayOfYear = ThreadLocalRandom.current().nextInt(1, gc.getActualMaximum(Calendar.DAY_OF_YEAR));
gc.set(Calendar.DAY_OF_YEAR, dayOfYear);
return gc.getTime();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2023-2023 the original author or authors.
*
* Licensed 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
*
* https://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 org.springframework.cloud.function.adapter.aws.web;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
//@SpringBootApplication
@Configuration
@Import({ PetsController.class })
public class PetStoreSpringAppConfig {
/*
* Create required HandlerMapping, to avoid several default HandlerMapping
* instances being created
*/
@Bean
public HandlerMapping handlerMapping() {
return new RequestMappingHandlerMapping();
}
/*
* Create required HandlerAdapter, to avoid several default HandlerAdapter
* instances being created
*/
@Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
@Bean
public Filter filter() {
return new Filter() {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("FILTER ===> Hello from: " + request.getLocalAddr());
chain.doFilter(request, response);
}
};
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2023-2023 the original author or authors.
*
* Licensed 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
*
* https://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 org.springframework.cloud.function.adapter.aws.web;
import java.security.Principal;
import java.util.Optional;
import java.util.UUID;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@RestController
@EnableWebMvc
public class PetsController {
@RequestMapping(path = "/pets", method = RequestMethod.POST)
public Pet createPet(@RequestBody Pet newPet) {
if (newPet.getName() == null || newPet.getBreed() == null) {
return null;
}
Pet dbPet = newPet;
dbPet.setId(UUID.randomUUID().toString());
return dbPet;
}
@RequestMapping(path = "/pets", method = RequestMethod.GET)
public Pet[] listPets(@RequestParam("limit") Optional<Integer> limit, Principal principal) {
int queryLimit = 10;
if (limit.isPresent()) {
queryLimit = limit.get();
}
Pet[] outputPets = new Pet[queryLimit];
for (int i = 0; i < queryLimit; i++) {
Pet newPet = new Pet();
newPet.setId(UUID.randomUUID().toString());
newPet.setName(PetData.getRandomName());
newPet.setBreed(PetData.getRandomBreed());
newPet.setDateOfBirth(PetData.getRandomDoB());
outputPets[i] = newPet;
}
return outputPets;
}
@RequestMapping(path = "/pets/{petId}", method = RequestMethod.GET)
public Pet listPets() {
System.out.println("=====> Getting pet by id");
Pet newPet = new Pet();
newPet.setId(UUID.randomUUID().toString());
newPet.setBreed(PetData.getRandomBreed());
newPet.setDateOfBirth(PetData.getRandomDoB());
newPet.setName(PetData.getRandomName());
return newPet;
}
}

View File

@@ -0,0 +1,88 @@
package org.springframework.cloud.function.adapter.aws.web;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
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.cloud.function.serverless.web.ProxyHttpServletRequest;
import org.springframework.cloud.function.serverless.web.ProxyHttpServletResponse;
import org.springframework.cloud.function.serverless.web.ProxyMvc;
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 = ProxyMvc.INSTANCE(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<List<Pet>> tr = new TypeReference<List<Pet>>() {
};
List<Pet> pets = mapper.readValue(response.getContentAsByteArray(), tr);
assertThat(pets.size()).isEqualTo(10);
assertThat(pets.get(0)).isInstanceOf(Pet.class);
}
@Test
public void validateGetListOfPojosWithParam() throws Exception {
ProxyHttpServletRequest request = new ProxyHttpServletRequest(null, "GET", "/pets");
request.setParameter("limit", "5");
ProxyHttpServletResponse response = new ProxyHttpServletResponse();
mvc.service(request, response);
TypeReference<List<Pet>> tr = new TypeReference<List<Pet>>() {
};
List<Pet> pets = mapper.readValue(response.getContentAsByteArray(), tr);
assertThat(pets.size()).isEqualTo(5);
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 validatePostWithBody() 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();
}
}