From a178f09ef49aaf21a3b8cc52690f3140302089b3 Mon Sep 17 00:00:00 2001 From: Pallavi Priyadarshani <50460471+pallavi-priyadarshani@users.noreply.github.com> Date: Tue, 23 Jul 2019 21:53:15 +0530 Subject: [PATCH] BAEL-3005 | Validating List objects in Spring --- spring-mvc-simple-2/pom.xml | 4 ++ .../SpringListValidationApplication.java | 17 +++++ .../constraint/CustomConstraint.java | 18 +++++ .../constraint/CustomConstraintValidator.java | 17 +++++ .../controller/MovieController.java | 54 +++++++++++++++ .../ConstraintViolationExceptionHandler.java | 30 +++++++++ .../listvalidation/model/Actor.java | 22 +++++++ .../listvalidation/model/Movie.java | 41 ++++++++++++ .../repository/MovieRepository.java | 57 ++++++++++++++++ .../MovieControllerIntegrationTest.java | 65 +++++++++++++++++++ 10 files changed, 325 insertions(+) create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java create mode 100644 spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java create mode 100644 spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java diff --git a/spring-mvc-simple-2/pom.xml b/spring-mvc-simple-2/pom.xml index 74302cff78..2fe93f1fae 100644 --- a/spring-mvc-simple-2/pom.xml +++ b/spring-mvc-simple-2/pom.xml @@ -18,6 +18,10 @@ org.springframework.boot spring-boot-starter-web + + org.projectlombok + lombok + diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java new file mode 100644 index 0000000000..f16d5f877f --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/SpringListValidationApplication.java @@ -0,0 +1,17 @@ +package com.baeldung.validation.listvalidation; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@ComponentScan(basePackages = "com.baeldung.validation.listvalidation") +@Configuration +@SpringBootApplication +public class SpringListValidationApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringListValidationApplication.class, args); + } + +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java new file mode 100644 index 0000000000..5d6e2213f1 --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraint.java @@ -0,0 +1,18 @@ +package com.baeldung.validation.listvalidation.constraint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.validation.Constraint; +import javax.validation.Payload; + +@Constraint(validatedBy = CustomConstraintValidator.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface CustomConstraint { + + String message() default "Invalid movie name."; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java new file mode 100644 index 0000000000..9f18a0c218 --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/constraint/CustomConstraintValidator.java @@ -0,0 +1,17 @@ +package com.baeldung.validation.listvalidation.constraint; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class CustomConstraintValidator implements ConstraintValidator { + + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (value == null || value.isEmpty()) { + return false; + } else { + return true; + } + } + +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java new file mode 100644 index 0000000000..6265fd50d0 --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/controller/MovieController.java @@ -0,0 +1,54 @@ +package com.baeldung.validation.listvalidation.controller; + +import java.util.List; + +import javax.validation.Valid; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindingResult; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PathVariable; +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.RestController; +import org.springframework.web.server.ResponseStatusException; + +import com.baeldung.validation.listvalidation.model.Movie; +import com.baeldung.validation.listvalidation.repository.MovieRepository; + +@Validated +@RestController +@RequestMapping("/movie") +public class MovieController { + + private final Logger LOGGER = LoggerFactory.getLogger(MovieController.class); + + @Autowired + private MovieRepository movieRepository; + + @RequestMapping(method = RequestMethod.POST) + public void add(@RequestBody @Valid Movie movie, BindingResult bindingResult) { + if (bindingResult.hasErrors()) { + StringBuilder builder = new StringBuilder(); + bindingResult.getAllErrors() + .forEach(error -> builder.append(" " + error.getDefaultMessage())); + LOGGER.error(builder.toString()); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, bindingResult.toString()); + } + movieRepository.add(movie); + } + + @RequestMapping(value = "/batch", method = RequestMethod.POST) + public void addAll(@RequestBody List<@Valid Movie> movie) { + movieRepository.addAll(movie); + } + + @RequestMapping(method = RequestMethod.GET, value = "/{name}") + public Movie get(@PathVariable String name) throws Exception { + return movieRepository.get(name); + } +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java new file mode 100644 index 0000000000..067a0e08ac --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/exception/ConstraintViolationExceptionHandler.java @@ -0,0 +1,30 @@ +package com.baeldung.validation.listvalidation.exception; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ConstraintViolationExceptionHandler { + private final Logger LOGGER = LoggerFactory.getLogger(ConstraintViolationExceptionHandler.class); + + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handle(ConstraintViolationException constraintViolationException) { + Set> violations = constraintViolationException.getConstraintViolations(); + StringBuilder builder = new StringBuilder(); + for (ConstraintViolation violation : violations) { + builder.append(violation.getMessage()); + + } + LOGGER.error(builder.toString()); + return new ResponseEntity(builder.toString(), HttpStatus.BAD_REQUEST); + } +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java new file mode 100644 index 0000000000..e6215f00c4 --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Actor.java @@ -0,0 +1,22 @@ +package com.baeldung.validation.listvalidation.model; + +import java.util.UUID; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class Actor { + + private String id; + private String name; + + public Actor(String name) { + this.id = UUID.randomUUID() + .toString(); + this.name = name; + } +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java new file mode 100644 index 0000000000..7ab4bfc34e --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/model/Movie.java @@ -0,0 +1,41 @@ +package com.baeldung.validation.listvalidation.model; + +import java.util.List; +import java.util.UUID; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.baeldung.validation.listvalidation.constraint.CustomConstraint; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class Movie { + + private String id; + + @CustomConstraint + private String name; + + @Size(min = 2, message = "Atleast 2 genres should be provided.") + private List genres; + + @NotNull(message = "Actor list cannot be null.") + @NotEmpty(message = "Actor list cannot be empty.") + private List actors; + + public Movie(String name, List genres, List actors) { + this.id = UUID.randomUUID() + .toString(); + this.name = name; + this.genres = genres; + this.actors = actors; + } + +} diff --git a/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java new file mode 100644 index 0000000000..67cfa6daca --- /dev/null +++ b/spring-mvc-simple-2/src/main/java/com/baeldung/validation/listvalidation/repository/MovieRepository.java @@ -0,0 +1,57 @@ +package com.baeldung.validation.listvalidation.repository; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Repository; + +import com.baeldung.validation.listvalidation.model.Actor; +import com.baeldung.validation.listvalidation.model.Movie; + +@Repository +public class MovieRepository { + private final Logger LOGGER = LoggerFactory.getLogger(MovieRepository.class); + static List moviesData; + + static { + moviesData = new ArrayList<>(); + + Actor a1 = new Actor("Actor1"); + + Movie m1 = new Movie("MovieABC", Arrays.asList("Drama"), Arrays.asList(a1)); + moviesData.add(m1); + + Movie m2 = new Movie("MovieDEF", Arrays.asList("Drama"), Arrays.asList(a1)); + moviesData.add(m2); + + } + + public void add(Movie movie) { + if (get(movie.getName()) == null) { + moviesData.add(movie); + LOGGER.info("Added new movie."); + } + } + + public Movie get(String name) { + Movie movie = null; + for (Movie m : moviesData) { + if (name.equalsIgnoreCase(m.getName())) { + movie = m; + LOGGER.info("Found movie with name " + name + ":" + movie); + } + } + + return movie; + } + + public void addAll(List movies) { + for (Movie movie : movies) { + add(movie); + } + } + +} diff --git a/spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java b/spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java new file mode 100644 index 0000000000..226e609d5f --- /dev/null +++ b/spring-mvc-simple-2/src/test/java/com/baeldung/validation/listvalidation/MovieControllerIntegrationTest.java @@ -0,0 +1,65 @@ +package com.baeldung.validation.listvalidation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import com.baeldung.validation.listvalidation.model.Actor; +import com.baeldung.validation.listvalidation.model.Movie; +import com.fasterxml.jackson.databind.ObjectMapper; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = SpringListValidationApplication.class) +@AutoConfigureMockMvc +public class MovieControllerIntegrationTest { + + @Autowired + private MockMvc mvc; + + ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void given1Genre_whenAddingMovie_thenThrowBadRequest() throws Exception { + Actor actor = new Actor("Actor1"); + Movie movie = new Movie("Movie1", Arrays.asList("Drama"), Arrays.asList(actor)); + mvc.perform(MockMvcRequestBuilders.post("/movie") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(objectMapper.writeValueAsString(movie))) + .andExpect(MockMvcResultMatchers.status() + .isBadRequest()); + } + + @Test + public void givenWithoutActor_whenAddingMovieList_thenThrowBadRequest() throws Exception { + List actors = new ArrayList<>(); + Movie movie = new Movie("Movie2", Arrays.asList("Action", "Thriller"), actors); + mvc.perform(MockMvcRequestBuilders.post("/movie/batch") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(objectMapper.writeValueAsString(Arrays.asList(movie)))) + .andExpect(MockMvcResultMatchers.status() + .isBadRequest()); + } + + @Test + public void givenEmptyMovieName_whenAddingMovie_thenThrowBadRequest() throws Exception { + Actor actor = new Actor("Actor1"); + Movie movie = new Movie("", Arrays.asList("Drama", "History"), Arrays.asList(actor)); + mvc.perform(MockMvcRequestBuilders.post("/movie") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(objectMapper.writeValueAsString(movie))) + .andExpect(MockMvcResultMatchers.status() + .isBadRequest()); + } + +}