From 6300c412904f9a36be6f40098ac7f6fa74b38b9b Mon Sep 17 00:00:00 2001 From: Niket Agrawal Date: Sun, 17 Sep 2023 22:36:14 +0530 Subject: [PATCH] BAEL-6695 Boolean Validation in Spring Boot BAEL-6695 Boolean Validation in Spring Boot --- spring-boot-modules/pom.xml | 1 + .../spring-boot-validations/pom.xml | 38 +++++++ .../main/java/com/baeldung/Application.java | 12 ++ .../controller/ValidationController.java | 19 ++++ .../GlobalExceptionHandler.java | 37 +++++++ .../deserializer/BooleanDeserializer.java | 18 +++ .../java/com/baeldung/dto/BooleanObject.java | 26 +++++ .../baeldung/service/ValidationService.java | 17 +++ .../ValidationControllerUnitTest.java | 104 ++++++++++++++++++ .../com/baeldung/dto/BooleanUnitTest.java | 28 +++++ .../service/ValidationServiceUnitTest.java | 35 ++++++ 11 files changed, 335 insertions(+) create mode 100644 spring-boot-modules/spring-boot-validations/pom.xml create mode 100644 spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java create mode 100644 spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java create mode 100644 spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java create mode 100644 spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java create mode 100644 spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java create mode 100644 spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java create mode 100644 spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java create mode 100644 spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java create mode 100644 spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/service/ValidationServiceUnitTest.java diff --git a/spring-boot-modules/pom.xml b/spring-boot-modules/pom.xml index 1c4b2bb38f..e0afbbddaf 100644 --- a/spring-boot-modules/pom.xml +++ b/spring-boot-modules/pom.xml @@ -104,6 +104,7 @@ spring-boot-springdoc-2 spring-boot-documentation spring-boot-3-url-matching + spring-boot-validations diff --git a/spring-boot-modules/spring-boot-validations/pom.xml b/spring-boot-modules/spring-boot-validations/pom.xml new file mode 100644 index 0000000000..e89c7f997b --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + com.baeldung.spring-boot-modules + spring-boot-modules + 1.0.0-SNAPSHOT + + spring-boot-validations + spring-boot-validations + Demo of Validations in Spring Boot + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java new file mode 100644 index 0000000000..c0490d50c6 --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/Application.java @@ -0,0 +1,12 @@ +package com.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java new file mode 100644 index 0000000000..8597c9617c --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controller/ValidationController.java @@ -0,0 +1,19 @@ +package com.baeldung.controller; + +import javax.validation.Valid; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.baeldung.dto.BooleanObject; + +@RestController +public class ValidationController { + + @PostMapping("/validateBoolean") + public ResponseEntity addUser(@RequestBody @Valid BooleanObject booleanObj) { + return ResponseEntity.ok("BooleanObject is valid"); + } +} diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java new file mode 100644 index 0000000000..601da40e23 --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/controlleradvice/GlobalExceptionHandler.java @@ -0,0 +1,37 @@ +package com.baeldung.controlleradvice; + +import java.util.stream.Collectors; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import com.fasterxml.jackson.databind.exc.InvalidFormatException; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public String handleValidationException(MethodArgumentNotValidException ex) { + return ex.getBindingResult() + .getFieldErrors() + .stream() + .map(e -> e.getDefaultMessage()) + .collect(Collectors.joining(",")); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public String handleIllegalArugmentException(IllegalArgumentException ex) { + return ex.getMessage(); + } + + @ExceptionHandler(InvalidFormatException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public String handleInvalidFormatException(InvalidFormatException ex) { + return ex.getOriginalMessage(); + } +} diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java new file mode 100644 index 0000000000..c9f30dc6e3 --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/deserializer/BooleanDeserializer.java @@ -0,0 +1,18 @@ +package com.baeldung.deserializer; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +public class BooleanDeserializer extends JsonDeserializer { + @Override + public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getText(); + if (value != null && !value.equals("TRUE") && !value.equals("FALSE")) { + throw new IllegalArgumentException("Boolean value must come as Capital TRUE or FALSE"); + } + return Boolean.valueOf(value); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java new file mode 100644 index 0000000000..77ca18b02c --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/dto/BooleanObject.java @@ -0,0 +1,26 @@ +package com.baeldung.dto; + +import javax.validation.constraints.AssertFalse; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotNull; + +import com.baeldung.deserializer.BooleanDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import lombok.Data; + +@Data +public class BooleanObject { + + @NotNull(message = "boolField cannot be null") + Boolean boolField; + + @AssertTrue(message = "trueField must have true value") + Boolean trueField; + + @AssertFalse(message = "falseField must have false value") + Boolean falseField; + + @JsonDeserialize(using = BooleanDeserializer.class) + Boolean boolStringVar; +} diff --git a/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java new file mode 100644 index 0000000000..5b96344e46 --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/main/java/com/baeldung/service/ValidationService.java @@ -0,0 +1,17 @@ +package com.baeldung.service; + +import javax.validation.Valid; + +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import com.baeldung.dto.BooleanObject; + +@Service +@Validated +public class ValidationService { + + public void validateBoolean(@Valid BooleanObject booleanObj) { + // further processing + } +} diff --git a/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java new file mode 100644 index 0000000000..78950e4c0e --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/controller/ValidationControllerUnitTest.java @@ -0,0 +1,104 @@ +package com.baeldung.controller; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import com.baeldung.dto.BooleanObject; +import com.fasterxml.jackson.databind.ObjectMapper; + +@ExtendWith(SpringExtension.class) +@WebMvcTest(controllers = ValidationController.class) +class ValidationControllerUnitTest { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mockMvc; + + @Test + void testNullBoolean() throws Exception { + BooleanObject boolObj = new BooleanObject(); + boolObj.setBoolField(null); + String postBody = objectMapper.writeValueAsString(boolObj); + + mockMvc.perform(post("/validateBoolean").contentType("application/json") + .content(postBody)) + .andExpect(status().isBadRequest()); + + } + + @Test + void testTrueBoolean() throws Exception { + BooleanObject boolObj = new BooleanObject(); + boolObj.setBoolField(Boolean.TRUE); + boolObj.setTrueField(Boolean.FALSE); + String postBody = objectMapper.writeValueAsString(boolObj); + + String output = mockMvc.perform(post("/validateBoolean").contentType("application/json") + .content(postBody)) + .andReturn() + .getResponse() + .getContentAsString(); + + assertEquals("trueField must have true value", output); + } + + @Test + void testFalseBoolean() throws Exception { + BooleanObject boolObj = new BooleanObject(); + boolObj.setBoolField(Boolean.TRUE); + boolObj.setTrueField(Boolean.TRUE); + boolObj.setFalseField(Boolean.TRUE); + String postBody = objectMapper.writeValueAsString(boolObj); + + String output = mockMvc.perform(post("/validateBoolean").contentType("application/json") + .content(postBody)) + .andReturn() + .getResponse() + .getContentAsString(); + + assertEquals("falseField must have false value", output); + } + + @Test + void testBooleanFromString() throws Exception { + BooleanObject boolObj = new BooleanObject(); + boolObj.setBoolField(Boolean.TRUE); + boolObj.setTrueField(Boolean.TRUE); + boolObj.setFalseField(Boolean.FALSE); + boolObj.setBoolStringVar(true); + String postBody = objectMapper.writeValueAsString(boolObj); + + String output = mockMvc.perform(post("/validateBoolean").contentType("application/json") + .content(postBody)) + .andReturn() + .getResponse() + .getContentAsString(); + + assertEquals("Boolean value must come as Capital TRUE or FALSE", output); + } + + @Test + void testValidBean() throws Exception { + + String postBody = "{\"boolField\":true,\"trueField\":true,\"falseField\":false,\"boolStringVar\":\"TRUE\"}"; + + String output = mockMvc.perform(post("/validateBoolean").contentType("application/json") + .content(postBody)) + .andReturn() + .getResponse() + .getContentAsString(); + + assertEquals("BooleanObject is valid", output); + + } +} diff --git a/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java new file mode 100644 index 0000000000..8c9375e72f --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/dto/BooleanUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.dto; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class BooleanUnitTest { + + @Test + void testBooleanFromString() { + Boolean trueVar = Boolean.valueOf("TRUE"); + Boolean falseVar = Boolean.valueOf("false"); + Boolean parsedVar = Boolean.parseBoolean("True"); + + assertEquals(Boolean.TRUE, trueVar); + assertEquals(Boolean.FALSE, falseVar); + assertEquals(Boolean.TRUE, parsedVar); + } + + @Test + void testBoolean() { + Boolean trueVar = Boolean.valueOf(true); + Boolean falseVar = Boolean.valueOf(false); + + assertEquals(Boolean.TRUE, trueVar); + assertEquals(Boolean.FALSE, falseVar); + } +} diff --git a/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/service/ValidationServiceUnitTest.java b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/service/ValidationServiceUnitTest.java new file mode 100644 index 0000000000..c67acea13d --- /dev/null +++ b/spring-boot-modules/spring-boot-validations/src/test/java/com/baeldung/service/ValidationServiceUnitTest.java @@ -0,0 +1,35 @@ +package com.baeldung.service; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import javax.validation.ConstraintViolationException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import com.baeldung.dto.BooleanObject; + +@ExtendWith(SpringExtension.class) +@SpringBootTest +class ValidationServiceUnitTest { + + @Autowired + ValidationService service; + + @Test + void validateBoolean() { + BooleanObject boolObj = new BooleanObject(); + boolObj.setBoolField(Boolean.valueOf(true)); + boolObj.setTrueField(Boolean.FALSE); + Throwable throwableError = assertThrows(ConstraintViolationException.class, () -> { + service.validateBoolean(boolObj); + }); + assertTrue(throwableError.getLocalizedMessage() + .endsWith("trueField must have true value")); + } + +}