From c2a3e2c7b9a06c6f7e9024066efc4032ccc94371 Mon Sep 17 00:00:00 2001 From: Connor Wooding <69477403+cwooding@users.noreply.github.com> Date: Mon, 3 Oct 2022 09:08:46 -0400 Subject: [PATCH] Add Commenting to Blog + Tests --- .../levvel/io/controller/BlogController.java | 22 +++++ .../levvel/io/data/CommentRepository.java | 9 ++ src/main/java/levvel/io/model/Comment.java | 24 +++++ .../java/levvel/io/model/CommentList.java | 21 +++++ .../java/levvel/io/service/BlogService.java | 9 ++ .../levvel/io/service/BlogServiceImpl.java | 34 ++++++++ src/main/resources/application.properties | 2 +- src/test/java/levvel/io/BlogTest.java | 34 ++++++++ .../levvel/io/WorkSampleApplicationTests.java | 25 +++++- .../io/controller/BlogControllerTest.java | 61 +++++++++++++ .../io/service/BlogServiceImplTest.java | 87 +++++++++++++++++++ 11 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 src/main/java/levvel/io/data/CommentRepository.java create mode 100644 src/main/java/levvel/io/model/Comment.java create mode 100644 src/main/java/levvel/io/model/CommentList.java create mode 100644 src/test/java/levvel/io/BlogTest.java create mode 100644 src/test/java/levvel/io/controller/BlogControllerTest.java create mode 100644 src/test/java/levvel/io/service/BlogServiceImplTest.java diff --git a/src/main/java/levvel/io/controller/BlogController.java b/src/main/java/levvel/io/controller/BlogController.java index dba2a87..6317f90 100644 --- a/src/main/java/levvel/io/controller/BlogController.java +++ b/src/main/java/levvel/io/controller/BlogController.java @@ -1,11 +1,15 @@ package levvel.io.controller; import levvel.io.model.Blog; +import levvel.io.model.Comment; +import levvel.io.model.CommentList; import levvel.io.service.BlogService; import lombok.AllArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @AllArgsConstructor @RequestMapping("/blog") @@ -24,4 +28,22 @@ public ResponseEntity getBlog(@PathVariable String id) { Blog blog = blogService.getBlog(id); return ResponseEntity.ok().body(blog); } + + @GetMapping("") + public ResponseEntity> getBlogs() { + List blogs = blogService.getBlogs(); + return ResponseEntity.ok().body(blogs); + } + + @PostMapping("/post/{id}/comment") + public ResponseEntity addComment(@PathVariable String id, @RequestBody Comment comment) { + blogService.addComment(id, comment); + return ResponseEntity.ok().body(comment); + } + + @GetMapping("/post/{id}/comment") + public ResponseEntity> getComments(@PathVariable String id) { + List comments = blogService.getComments(id); + return ResponseEntity.ok().body(comments); + } } diff --git a/src/main/java/levvel/io/data/CommentRepository.java b/src/main/java/levvel/io/data/CommentRepository.java new file mode 100644 index 0000000..65e9e4f --- /dev/null +++ b/src/main/java/levvel/io/data/CommentRepository.java @@ -0,0 +1,9 @@ +package levvel.io.data; + +import levvel.io.model.CommentList; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentRepository extends MongoRepository { +} diff --git a/src/main/java/levvel/io/model/Comment.java b/src/main/java/levvel/io/model/Comment.java new file mode 100644 index 0000000..91d9f7e --- /dev/null +++ b/src/main/java/levvel/io/model/Comment.java @@ -0,0 +1,24 @@ +package levvel.io.model; + +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.LastModifiedDate; + +import java.time.LocalDateTime; + +@Data +public class Comment { + + @Id + String id; + + @CreatedDate + private LocalDateTime createdDate; + + @LastModifiedDate + private LocalDateTime lastModifiedDate; + + String author; + String text; +} diff --git a/src/main/java/levvel/io/model/CommentList.java b/src/main/java/levvel/io/model/CommentList.java new file mode 100644 index 0000000..eb5a935 --- /dev/null +++ b/src/main/java/levvel/io/model/CommentList.java @@ -0,0 +1,21 @@ +package levvel.io.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.data.annotation.Id; + +import java.util.List; + +@Data +@AllArgsConstructor +public class CommentList { + + @Id + String blogId; + + List comments; + + public void addComment(Comment comment) { + comments.add(comment); + } +} diff --git a/src/main/java/levvel/io/service/BlogService.java b/src/main/java/levvel/io/service/BlogService.java index b1e4b41..f4e9e71 100644 --- a/src/main/java/levvel/io/service/BlogService.java +++ b/src/main/java/levvel/io/service/BlogService.java @@ -1,10 +1,19 @@ package levvel.io.service; import levvel.io.model.Blog; +import levvel.io.model.Comment; + +import java.util.List; public interface BlogService { void addBlog(Blog blog); Blog getBlog(String id); + + List getBlogs(); + + void addComment(String id, Comment comment); + + List getComments(String id); } diff --git a/src/main/java/levvel/io/service/BlogServiceImpl.java b/src/main/java/levvel/io/service/BlogServiceImpl.java index 0880819..5553437 100644 --- a/src/main/java/levvel/io/service/BlogServiceImpl.java +++ b/src/main/java/levvel/io/service/BlogServiceImpl.java @@ -1,23 +1,57 @@ package levvel.io.service; import levvel.io.data.BlogRepository; +import levvel.io.data.CommentRepository; import levvel.io.model.Blog; +import levvel.io.model.Comment; +import levvel.io.model.CommentList; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + @Service @AllArgsConstructor public class BlogServiceImpl implements BlogService { private BlogRepository blogRepository; + private CommentRepository commentRepository; @Override public void addBlog(Blog blog) { + // Save the blog blogRepository.save(blog); + + // Create a comment section for this Blog + CommentList commentList = new CommentList(blog.getId(), new ArrayList<>()); + commentRepository.save(commentList); } @Override public Blog getBlog(String id) { + // Get the blog from the id return blogRepository.findById(id).orElseGet(null); } + + @Override + public List getBlogs() { + // Function to get all blogs, useful for debugging + return blogRepository.findAll(); + } + + @Override + public void addComment(String id, Comment comment) { + // Get current comment list for this blog and append comment to it + CommentList commentList = commentRepository.findById(id).orElseGet(null); + commentList.addComment(comment); + commentRepository.save(commentList); + } + + @Override + public List getComments(String id) { + // Get the object and extract the list of comments + CommentList commentList = commentRepository.findById(id).orElseGet(null); + return commentList.getComments(); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8b13789..f620381 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1 @@ - +server.port=8090 diff --git a/src/test/java/levvel/io/BlogTest.java b/src/test/java/levvel/io/BlogTest.java new file mode 100644 index 0000000..8c68844 --- /dev/null +++ b/src/test/java/levvel/io/BlogTest.java @@ -0,0 +1,34 @@ +package levvel.io; + +import com.fasterxml.jackson.databind.ObjectMapper; +import levvel.io.model.Blog; +import levvel.io.model.Comment; + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +public abstract class BlogTest { + + protected static final String ID = "fake_id"; + + protected Blog getBlogFromResource() throws IOException { + File file = new File( + Objects.requireNonNull(this.getClass().getClassLoader().getResource("addBlog.json")).getFile() + ); + + ObjectMapper mapper = new ObjectMapper(); + + return mapper.readValue(file, Blog.class); + } + + protected Comment getCommentFromResource() throws IOException { + File file = new File( + Objects.requireNonNull(this.getClass().getClassLoader().getResource("addComment.json")).getFile() + ); + + ObjectMapper mapper = new ObjectMapper(); + + return mapper.readValue(file, Comment.class); + } +} diff --git a/src/test/java/levvel/io/WorkSampleApplicationTests.java b/src/test/java/levvel/io/WorkSampleApplicationTests.java index 50d3cef..88985ee 100644 --- a/src/test/java/levvel/io/WorkSampleApplicationTests.java +++ b/src/test/java/levvel/io/WorkSampleApplicationTests.java @@ -1,13 +1,34 @@ package levvel.io; +import levvel.io.controller.BlogController; +import levvel.io.data.BlogRepository; +import levvel.io.data.CommentRepository; +import levvel.io.service.BlogService; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; + @SpringBootTest class WorkSampleApplicationTests { + @Autowired + private BlogController blogController; + + @Autowired + private BlogService blogService; + + @Autowired + private BlogRepository blogRepository; + + @Autowired + private CommentRepository commentRepository; + @Test - void contextLoads() { + public void contextLoads() { + assert(blogController != null); + assert(blogService != null); + assert(blogRepository != null); + assert(commentRepository != null); } - } diff --git a/src/test/java/levvel/io/controller/BlogControllerTest.java b/src/test/java/levvel/io/controller/BlogControllerTest.java new file mode 100644 index 0000000..e870cba --- /dev/null +++ b/src/test/java/levvel/io/controller/BlogControllerTest.java @@ -0,0 +1,61 @@ +package levvel.io.controller; + +import levvel.io.BlogTest; +import levvel.io.model.Blog; +import levvel.io.model.Comment; +import levvel.io.service.BlogService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.ResponseEntity; + +import java.io.IOException; +import java.util.List; + +import static org.mockito.Mockito.mock; + +public class BlogControllerTest extends BlogTest { + + private BlogController blogController; + + private Blog blog; + private Comment comment; + + @BeforeEach + void setup() throws IOException { + BlogService blogService = mock(BlogService.class); + + blogController = new BlogController(blogService); + + blog = getBlogFromResource(); + comment = getCommentFromResource(); + } + + @Test + void testAddBlog() { + ResponseEntity response = blogController.addBlog(blog); + + Assertions.assertEquals(response.getStatusCodeValue(), 200); + } + + @Test + void testGetBlog() { + ResponseEntity response = blogController.getBlog(ID); + + Assertions.assertEquals(response.getStatusCodeValue(), 200); + } + + @Test + void testAddComment() { + ResponseEntity response = blogController.addComment(ID, comment); + + Assertions.assertEquals(response.getStatusCodeValue(), 200); + } + + @Test + void testGetComment() { + ResponseEntity> response = blogController.getComments(ID); + + Assertions.assertEquals(response.getStatusCodeValue(), 200); + } +} diff --git a/src/test/java/levvel/io/service/BlogServiceImplTest.java b/src/test/java/levvel/io/service/BlogServiceImplTest.java new file mode 100644 index 0000000..1bdcdf3 --- /dev/null +++ b/src/test/java/levvel/io/service/BlogServiceImplTest.java @@ -0,0 +1,87 @@ +package levvel.io.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import levvel.io.BlogTest; +import levvel.io.data.BlogRepository; +import levvel.io.data.CommentRepository; +import levvel.io.model.Blog; +import levvel.io.model.Comment; +import levvel.io.model.CommentList; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static java.util.Optional.ofNullable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class BlogServiceImplTest extends BlogTest { + + private BlogService blogService; + + private BlogRepository blogRepository; + private CommentRepository commentRepository; + + private Blog blog; + private Comment comment; + private CommentList commentList; + + @BeforeEach + void setup() throws IOException { + blogRepository = mock(BlogRepository.class); + commentRepository = mock(CommentRepository.class); + + blogService = new BlogServiceImpl(blogRepository, commentRepository); + + blog = getBlogFromResource(); + comment = getCommentFromResource(); + + commentList = new CommentList(blog.getId(), new ArrayList<>()); + } + + @Test + void testAddBlog() { + blogService.addBlog(blog); + + verify(blogRepository).save(any()); + verify(commentRepository).save(any()); + } + + @Test + void testGetBlog() { + when(blogRepository.findById(any())).thenReturn(ofNullable(blog)); + + Blog returnedBlog = blogService.getBlog(ID); + + verify(blogRepository).findById(ID); + + Assertions.assertEquals(blog, returnedBlog); + } + + @Test + void testAddComment() { + when(commentRepository.findById(any())).thenReturn(ofNullable(commentList)); + + blogService.addComment(ID, comment); + + verify(commentRepository).findById(any()); + verify(commentRepository).save(any()); + } + + @Test + void testGetComments() { + when(commentRepository.findById(any())).thenReturn(ofNullable(commentList)); + + List comments = blogService.getComments(ID); + + verify(commentRepository).findById(any()); + + Assertions.assertEquals(comments, commentList.getComments()); + } +}