From 38955467e122df82f7d490c5e4d06ab4dadc17c0 Mon Sep 17 00:00:00 2001 From: Ralf Ueberfuhr Date: Wed, 20 Dec 2023 10:27:40 +0100 Subject: [PATCH] add validation --- http-requests/POST-blogposts.http | 16 ++++++++++++++++ pom.xml | 4 ++++ .../blog/boundary/GlobalExceptionHandler.java | 19 +++++++++++++++++++ .../schulung/spring/blog/domain/BlogPost.java | 6 ++++++ .../spring/blog/domain/BlogPostService.java | 6 +++++- .../blog/boundary/BlogPostApiTests.java | 16 ++++++++++++++++ .../BlogPostApiWithMockedServiceTests.java | 19 +++++++++++++++++++ 7 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java diff --git a/http-requests/POST-blogposts.http b/http-requests/POST-blogposts.http index 9c8703c..be58461 100644 --- a/http-requests/POST-blogposts.http +++ b/http-requests/POST-blogposts.http @@ -12,3 +12,19 @@ Content-Type: application/json client.assert(response.status === 201); }); %} + +### + +POST {{endpoint}}/blogposts +Accept: application/json +Content-Type: application/json + +{ + "content": "Das ist ein Test" +} + +> {% + client.test("BlogPosts returned", () => { + client.assert(response.status === 400); + }); +%} diff --git a/pom.xml b/pom.xml index 02c2c38..ddba5a1 100644 --- a/pom.xml +++ b/pom.xml @@ -30,6 +30,10 @@ org.springframework.boot spring-boot-starter-hateoas + + org.springframework.boot + spring-boot-starter-validation + org.springframework.boot diff --git a/src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java b/src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java new file mode 100644 index 0000000..5ed59f5 --- /dev/null +++ b/src/main/java/de/sample/schulung/spring/blog/boundary/GlobalExceptionHandler.java @@ -0,0 +1,19 @@ +package de.sample.schulung.spring.blog.boundary; + +import jakarta.validation.ConstraintViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(ConstraintViolationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + void handleValidationException() { + // do nothing + // return ResponseEntity, Fehler-Object (JSON), ProblemDetail + } + +} diff --git a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java index 30b0c4b..0469cc8 100644 --- a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java +++ b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPost.java @@ -1,5 +1,7 @@ package de.sample.schulung.spring.blog.domain; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -13,7 +15,11 @@ public class BlogPost { private UUID id; + @Size(min = 3) + @NotNull private String title; + @Size(min = 10) + @NotNull private String content; private LocalDateTime timestamp; diff --git a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java index 29bc422..a595527 100644 --- a/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java +++ b/src/main/java/de/sample/schulung/spring/blog/domain/BlogPostService.java @@ -1,6 +1,9 @@ package de.sample.schulung.spring.blog.domain; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.HashMap; @@ -9,6 +12,7 @@ import java.util.UUID; import java.util.stream.Stream; +@Validated @Service public class BlogPostService { @@ -24,7 +28,7 @@ public Optional findById(UUID id) { ); } - public void create(BlogPost blogPost) { + public void create(@Valid @NotNull BlogPost blogPost) { final var id = UUID.randomUUID(); blogPost.setId(id); blogPost.setTimestamp(LocalDateTime.now()); diff --git a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java index a6f2a1b..d6a8fa1 100644 --- a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java +++ b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiTests.java @@ -62,6 +62,22 @@ void shouldCreateBlogPostSuccessfully() throws Exception { } + @Test + void shouldNotCreateInvalidBlogPost() throws Exception { + mvc.perform( + post("/blogposts") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "content": "Das ist ein Test" + } + """) + ) + .andExpect(status().isBadRequest()); + + } + /* * Testfall: * - Lege BlogPost an (title: "test") diff --git a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java index 836d14e..651cbbc 100644 --- a/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java +++ b/src/test/java/de/sample/schulung/spring/blog/boundary/BlogPostApiWithMockedServiceTests.java @@ -14,8 +14,10 @@ import java.util.Optional; import java.util.UUID; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -58,6 +60,23 @@ void shouldReturnSingleBlogPostSuccessfully() throws Exception { .andExpect(jsonPath("$.timestamp").exists()); } + @Test + void shouldNotCreateInvalidBlogPost() throws Exception { + mvc.perform( + post("/blogposts") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "content": "Das ist ein Test" + } + """) + ) + .andExpect(status().isBadRequest()); + verifyNoInteractions(service); + + } + // TODO wenn ID im Request mitgeschickt wird -> ??