From 628df100287ba677606aaee7fcd2291d144702dd Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Wed, 1 May 2024 20:26:49 +0700 Subject: [PATCH 1/7] [FIX] Fix CD workflow --- .github/workflows/cd.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 47d1fb0..ebdb71d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -76,6 +76,16 @@ jobs: name: Publish Docker Image runs-on: ubuntu-latest needs: test + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: snackscription_review + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Checkout Repository uses: actions/checkout@v4 From 9c61cfde65a30db203e45f9ff87e9a1efb06daa2 Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Wed, 8 May 2024 20:55:24 +0700 Subject: [PATCH 2/7] [REFACTOR] simplify controller API endpoint path --- .../review/controller/ReviewController.java | 20 +++++++++---------- .../controller/ReviewControllerTest.java | 16 +++++++-------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/snackscription/review/controller/ReviewController.java b/src/main/java/snackscription/review/controller/ReviewController.java index 4b89ab2..05db339 100644 --- a/src/main/java/snackscription/review/controller/ReviewController.java +++ b/src/main/java/snackscription/review/controller/ReviewController.java @@ -43,7 +43,7 @@ public ResponseEntity reviewPage() { return ResponseEntity.ok().body("Welcome to the review service!"); } - @PostMapping("/api/subscription-boxes/{subscriptionBoxId}") + @PostMapping("/subscription-boxes/{subscriptionBoxId}") public ResponseEntity createSubscriptionBoxReview(@RequestBody Map body, @PathVariable String subscriptionBoxId) { try { @@ -58,7 +58,7 @@ public ResponseEntity createSubscriptionBoxReview(@RequestBody Map> getAllPublicSubscriptionBoxReview(@PathVariable String subscriptionBoxId) { try { List reviews = reviewService.getAllSubscriptionBoxReview(subscriptionBoxId, "APPROVED"); @@ -68,7 +68,7 @@ public ResponseEntity> getAllPublicSubscriptionBoxReview(@PathVaria } } - @GetMapping("/api/subscription-boxes/{subscriptionBoxId}/users/self") + @GetMapping("/subscription-boxes/{subscriptionBoxId}/users/self") public ResponseEntity getSelfSubscriptionBoxReview(@RequestBody Map body, @PathVariable String subscriptionBoxId) { try { String userId = body.get("userId"); @@ -79,7 +79,7 @@ public ResponseEntity getSelfSubscriptionBoxReview(@RequestBody Map editSelfSubscriptionBoxId(@RequestBody Map body, @PathVariable String subscriptionBoxId) { try { String userId = body.get("userId"); @@ -93,7 +93,7 @@ public ResponseEntity editSelfSubscriptionBoxId(@RequestBody Map deleteSelfSubscriptionBoxReview(@RequestBody Map body, @PathVariable String subscriptionBoxId) { try { String userId = body.get("userId"); @@ -104,7 +104,7 @@ public ResponseEntity deleteSelfSubscriptionBoxReview(@RequestBody Map deleteSubscriptionBoxReview(@PathVariable String subscriptionBoxId, @PathVariable String userId) { try { reviewService.deleteReview(subscriptionBoxId, userId); @@ -114,17 +114,17 @@ public ResponseEntity deleteSubscriptionBoxReview(@PathVariable String s } } - @GetMapping("/api/reviews/{subsboxId}") + @GetMapping("/reviews/{subsboxId}") public List getBySubscriptionBoxId(@PathVariable String subsboxId) throws Exception { return reviewService.getAllSubscriptionBoxReview(subsboxId, null); } - @GetMapping("/api/reviews/{reviewId}") + @GetMapping("/reviews/{reviewId}") public Review getById(@PathVariable String reviewId) throws Exception { return reviewService.findById(reviewId); } - @PutMapping("/api/reviews/{reviewId}/approve") + @PutMapping("/reviews/{reviewId}/approve") public ResponseEntity approveReview(@PathVariable String reviewId) { try { Review review = reviewService.approveReview(reviewId); @@ -134,7 +134,7 @@ public ResponseEntity approveReview(@PathVariable String reviewId) { } } - @PutMapping("/api/reviews/{reviewId}/reject") + @PutMapping("/reviews/{reviewId}/reject") public ResponseEntity rejectReview(@PathVariable String reviewId) { try { Review review = reviewService.rejectReview(reviewId); diff --git a/src/test/java/snackscription/review/controller/ReviewControllerTest.java b/src/test/java/snackscription/review/controller/ReviewControllerTest.java index 764efdc..fa4260a 100644 --- a/src/test/java/snackscription/review/controller/ReviewControllerTest.java +++ b/src/test/java/snackscription/review/controller/ReviewControllerTest.java @@ -78,7 +78,7 @@ public void testCreateSubscriptionBoxReview() throws Exception{ when(reviewService.createReview(review.getRating(), review.getContent(), review.getSubscriptionBoxId(), review.getUserId())).thenReturn(review); - ResultActions result = mockMvc.perform(post("/api/subscription-boxes/{subscriptionBoxId}", review.getSubscriptionBoxId()) + ResultActions result = mockMvc.perform(post("/subscription-boxes/{subscriptionBoxId}", review.getSubscriptionBoxId()) .contentType(MediaType.APPLICATION_JSON) .content("{\"rating\": 5, \"content\": \"I love it\", \"userId\": \"user_123\"}")) .andExpect(status().isCreated()) @@ -102,7 +102,7 @@ public void testReadAllPublicSubscriptionBoxReview() throws Exception { when(reviewService.getAllSubscriptionBoxReview(subsboxId, "APPROVED")).thenReturn(approvedReviews); - String result = mockMvc.perform(get("/api/subscription-boxes/{subscriptionBoxId}", subsboxId)) + String result = mockMvc.perform(get("/subscription-boxes/{subscriptionBoxId}", subsboxId)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(approvedReviews.size()))) .andReturn() @@ -149,7 +149,7 @@ public void readSelfSubscriptionBoxReview() throws Exception { when(reviewService.getReview(subsboxId, userId)).thenReturn(review); - ResultActions result = mockMvc.perform(get("/api/subscription-boxes/{subscriptionBoxId}/users/self", subsboxId) + ResultActions result = mockMvc.perform(get("/subscription-boxes/{subscriptionBoxId}/users/self", subsboxId) .contentType(MediaType.APPLICATION_JSON) .content("{\"userId\": \"user_123\"}")) .andExpect(status().isOk()) @@ -171,7 +171,7 @@ public void testEditSelfSubscriptionBoxReview() throws Exception { String newContent = "Awikwok"; when(reviewService.editReview(newRating, newContent, subsboxId, userId)).thenReturn(new Review(newRating, newContent, userId, subsboxId)); - ResultActions result = mockMvc.perform(put("/api/subscription-boxes/{subscriptionBoxId}/users/self", subsboxId) + ResultActions result = mockMvc.perform(put("/subscription-boxes/{subscriptionBoxId}/users/self", subsboxId) .contentType(MediaType.APPLICATION_JSON) .content("{\"rating\": 4, \"content\": \"Awikwok\", \"userId\": \"user_123\"}")) .andExpect(status().isOk()) @@ -191,7 +191,7 @@ public void testDeleteSelfSubscriptionBoxReview() throws Exception { doNothing().when(reviewService).deleteReview(subsboxId, userId); - ResultActions result = mockMvc.perform(delete("/api/subscription-boxes/{subscriptionBoxId}/users/self", subsboxId) + ResultActions result = mockMvc.perform(delete("/subscription-boxes/{subscriptionBoxId}/users/self", subsboxId) .contentType(MediaType.APPLICATION_JSON) .content("{\"userId\": \"user_123\"}")) .andExpect(status().isNoContent()); @@ -207,7 +207,7 @@ public void testDeleteUserSubscriptionBoxReview() throws Exception { doNothing().when(reviewService).deleteReview(subsboxId, userId); - ResultActions result = mockMvc.perform(delete("/api/subscription-boxes/{subscriptionBoxId}/users/{userId}", subsboxId, userId) + ResultActions result = mockMvc.perform(delete("/subscription-boxes/{subscriptionBoxId}/users/{userId}", subsboxId, userId) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNoContent()); @@ -224,7 +224,7 @@ public void testApproveReview() throws Exception { when(reviewService.approveReview(reviewId)).thenReturn(approvedReview); - ResultActions result = mockMvc.perform(put("/api/reviews/{reviewId}/approve", reviewId)) + ResultActions result = mockMvc.perform(put("/reviews/{reviewId}/approve", reviewId)) .andExpect(status().isOk()) .andExpect(jsonPath("$.rating", is(review.getRating()))) .andExpect(jsonPath("$.content", is(review.getContent()))) @@ -245,7 +245,7 @@ public void testRejectReview() throws Exception { when(reviewService.rejectReview(reviewId)).thenReturn(rejectedReview); - ResultActions result = mockMvc.perform(put("/api/reviews/{reviewId}/reject", reviewId)) + ResultActions result = mockMvc.perform(put("/reviews/{reviewId}/reject", reviewId)) .andExpect(status().isOk()) .andExpect(jsonPath("$.rating", is(review.getRating()))) .andExpect(jsonPath("$.content", is(review.getContent()))) From 124e622745d9480c2e83b90d0a9bb0976afec1ca Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Wed, 15 May 2024 15:24:08 +0700 Subject: [PATCH 3/7] [REFACTOR] Rename APO endpoint path, rename user to author, change review id attribute to composite of (subsbox, author) --- gradlew | 2 +- .../review/controller/ReviewController.java | 101 ++++---- .../snackscription/review/model/Review.java | 34 ++- .../snackscription/review/model/ReviewId.java | 25 ++ .../review/repository/ReviewRepository.java | 14 +- .../review/service/ReviewService.java | 65 +++--- src/main/resources/application-dev.properties | 2 +- .../controller/ReviewControllerTest.java | 217 ++++++++---------- .../review/model/ReviewTest.java | 6 +- .../repository/ReviewRepositoryTest.java | 36 ++- .../review/service/ReviewServiceTest.java | 140 ++++------- 11 files changed, 275 insertions(+), 367 deletions(-) create mode 100644 src/main/java/snackscription/review/model/ReviewId.java diff --git a/gradlew b/gradlew index 1aa94a4..e7c6edb 100755 --- a/gradlew +++ b/gradlew @@ -205,7 +205,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# * For example: A author cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ diff --git a/src/main/java/snackscription/review/controller/ReviewController.java b/src/main/java/snackscription/review/controller/ReviewController.java index 05db339..d126faa 100644 --- a/src/main/java/snackscription/review/controller/ReviewController.java +++ b/src/main/java/snackscription/review/controller/ReviewController.java @@ -1,34 +1,17 @@ package snackscription.review.controller; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.apache.catalina.connector.Response; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Resource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + import snackscription.review.model.Review; -import snackscription.review.repository.ReviewRepository; import snackscription.review.service.ReviewService; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.PutMapping; - - - - @RestController -@RequestMapping("/") +@RequestMapping("/reviews") public class ReviewController { @@ -43,101 +26,95 @@ public ResponseEntity reviewPage() { return ResponseEntity.ok().body("Welcome to the review service!"); } - @PostMapping("/subscription-boxes/{subscriptionBoxId}") - public ResponseEntity createSubscriptionBoxReview(@RequestBody Map body, @PathVariable String subscriptionBoxId) { - + @PostMapping("/subscription-boxes/{subsbox}") + public ResponseEntity createSubsboxReview(@RequestBody Map body, @PathVariable String subsbox) { try { - String userId = body.get("userId"); + String author = body.get("author"); int rating = Integer.parseInt(body.get("rating")); String content = body.get("content"); - Review review = reviewService.createReview(rating, content, subscriptionBoxId, userId); + Review review = reviewService.createReview(rating, content, subsbox, author); return new ResponseEntity<>(review, HttpStatus.CREATED); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } - @GetMapping("/subscription-boxes/{subscriptionBoxId}") - public ResponseEntity> getAllPublicSubscriptionBoxReview(@PathVariable String subscriptionBoxId) { + @GetMapping("/subscription-boxes/{subsbox}/public") + public ResponseEntity> getPublicSubsboxReview(@PathVariable String subsbox) { try { - List reviews = reviewService.getAllSubscriptionBoxReview(subscriptionBoxId, "APPROVED"); + List reviews = reviewService.getSubsboxReview(subsbox, "APPROVED"); return new ResponseEntity<>(reviews, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } - @GetMapping("/subscription-boxes/{subscriptionBoxId}/users/self") - public ResponseEntity getSelfSubscriptionBoxReview(@RequestBody Map body, @PathVariable String subscriptionBoxId) { + @GetMapping("/subscription-boxes/{subsbox}/users/{user}") + public ResponseEntity getSelfSubsboxReview(@RequestBody Map body, @PathVariable String subsbox, @PathVariable String user) { try { - String userId = body.get("userId"); - Review review = reviewService.getReview(subscriptionBoxId, userId); + String sender = body.get("author"); // TODO: nanti pakai JWT token untuk ambil sendernya + if (!authenticate(sender, user)) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + Review review = reviewService.getReview(subsbox, user); return new ResponseEntity<>(review, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } - @PutMapping("/subscription-boxes/{subscriptionBoxId}/users/self") - public ResponseEntity editSelfSubscriptionBoxId(@RequestBody Map body, @PathVariable String subscriptionBoxId) { + @PutMapping("/subscription-boxes/{subsbox}/users/{user}") + public ResponseEntity editReview(@RequestBody Map body, @PathVariable String subsbox, @PathVariable String user) { try { - String userId = body.get("userId"); + String sender = body.get("author"); // TODO: nanti pakai JWT token untuk ambil sendernya + if (!authenticate(sender, user)) { + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + int rating = Integer.parseInt(body.get("rating")); String content = body.get("content"); - Review review = reviewService.editReview(rating, content, subscriptionBoxId, userId); + Review review = reviewService.editReview(rating, content, subsbox, user); return new ResponseEntity<>(review, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } - @DeleteMapping("/subscription-boxes/{subscriptionBoxId}/users/self") - public ResponseEntity deleteSelfSubscriptionBoxReview(@RequestBody Map body, @PathVariable String subscriptionBoxId) { - try { - String userId = body.get("userId"); - reviewService.deleteReview(subscriptionBoxId, userId); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } catch (Exception e) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } + private boolean authenticate(String sender, String user) { + return true; } - @DeleteMapping("/subscription-boxes/{subscriptionBoxId}/users/{userId}") - public ResponseEntity deleteSubscriptionBoxReview(@PathVariable String subscriptionBoxId, @PathVariable String userId) { + @DeleteMapping("/subscription-boxes/{subsbox}/users/{user}") + public ResponseEntity deleteReview(@PathVariable String subsbox, @PathVariable String user) { try { - reviewService.deleteReview(subscriptionBoxId, userId); + reviewService.deleteReview(subsbox, user); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } - @GetMapping("/reviews/{subsboxId}") - public List getBySubscriptionBoxId(@PathVariable String subsboxId) throws Exception { - return reviewService.getAllSubscriptionBoxReview(subsboxId, null); - } - - @GetMapping("/reviews/{reviewId}") - public Review getById(@PathVariable String reviewId) throws Exception { - return reviewService.findById(reviewId); + @GetMapping("/subscription-boxes/{subsbox}") + public List getSubsboxReview(@PathVariable String subsbox) throws Exception { + return reviewService.getSubsboxReview(subsbox, null); } - @PutMapping("/reviews/{reviewId}/approve") - public ResponseEntity approveReview(@PathVariable String reviewId) { + @PutMapping("/subscription-boxes/{subsbox}/users/{user}/approve") + public ResponseEntity approveReview(@PathVariable String subsbox, @PathVariable String user) { try { - Review review = reviewService.approveReview(reviewId); + Review review = reviewService.approveReview(subsbox, user); return new ResponseEntity<>(review, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } - @PutMapping("/reviews/{reviewId}/reject") - public ResponseEntity rejectReview(@PathVariable String reviewId) { + @PutMapping("/subscription-boxes/{subsbox}/users/{user}/reject") + public ResponseEntity rejectReview(@PathVariable String subsbox, @PathVariable String user) { try { - Review review = reviewService.rejectReview(reviewId); + Review review = reviewService.rejectReview(subsbox, user); return new ResponseEntity<>(review, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); diff --git a/src/main/java/snackscription/review/model/Review.java b/src/main/java/snackscription/review/model/Review.java index 572d7a5..0ef1c69 100644 --- a/src/main/java/snackscription/review/model/Review.java +++ b/src/main/java/snackscription/review/model/Review.java @@ -1,19 +1,15 @@ package snackscription.review.model; -import lombok.Getter; -import lombok.Setter; - -import java.util.UUID; +import lombok.Data; import jakarta.persistence.*; -@Getter -@Setter +@Data @Entity -@Table(name = "review") +@Table public class Review { - @Id - private String id; + @EmbeddedId + private ReviewId id; @Column(name = "rating", nullable = false) private int rating; @@ -24,22 +20,14 @@ public class Review { @Column(name = "state", nullable = false) private ReviewState state; - @Column(name="user_id", nullable = false) - private String userId; - - @Column(name="subsbox_id", nullable = false) - private String subscriptionBoxId; - public Review() { } - public Review(int rating, String content, String userId, String subscriptionBoxId) { - this.id = UUID.randomUUID().toString(); + public Review(int rating, String content, String subsbox, String user) { + this.id = new ReviewId(subsbox, user); this.rating = rating; this.content = content; this.state = ReviewState.PENDING; - this.userId = userId; - this.subscriptionBoxId = subscriptionBoxId; } public void editReview(int rating, String content) { @@ -61,4 +49,12 @@ public void approve() { public void reject() { this.state.reject(this); } + + public String getSubsbox() { + return this.id.getSubsbox(); + } + + public String getAuthor() { + return this.id.getAuthor(); + } } \ No newline at end of file diff --git a/src/main/java/snackscription/review/model/ReviewId.java b/src/main/java/snackscription/review/model/ReviewId.java new file mode 100644 index 0000000..d1656fb --- /dev/null +++ b/src/main/java/snackscription/review/model/ReviewId.java @@ -0,0 +1,25 @@ +package snackscription.review.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.Data; + +import java.io.Serializable; + +@Data +@Embeddable +public class ReviewId implements Serializable { + @Column(name = "subsbox", nullable = false) + private String subsbox; + + @Column(name = "author", nullable = false) + private String author; + + public ReviewId(String subsbox, String author) { + this.subsbox = subsbox; + this.author = author; + } + public ReviewId() { + + } +} diff --git a/src/main/java/snackscription/review/repository/ReviewRepository.java b/src/main/java/snackscription/review/repository/ReviewRepository.java index bb448bf..096b317 100644 --- a/src/main/java/snackscription/review/repository/ReviewRepository.java +++ b/src/main/java/snackscription/review/repository/ReviewRepository.java @@ -2,14 +2,14 @@ import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; import snackscription.review.model.Review; +import snackscription.review.model.ReviewId; import snackscription.review.model.ReviewState; -public interface ReviewRepository extends JpaRepository { - List findBySubscriptionBoxId(String subsboxId); - List findBySubscriptionBoxIdAndState(String subsboxId, ReviewState state); - Review findBySubscriptionBoxIdAndUserId(String subsboxId, String userId); - void deleteBySubscriptionBoxIdAndUserId(String subsboxId, String userId); +public interface ReviewRepository extends JpaRepository { + List findByIdSubsbox(String subsbox); + List findByIdAuthor(String author); + List findByIdSubsboxAndState(String subsbox, ReviewState state); + Review findByIdSubsboxAndIdAuthor(String subsbox, String author); + void deleteByIdSubsboxAndIdAuthor(String subsbox, String author); } \ No newline at end of file diff --git a/src/main/java/snackscription/review/service/ReviewService.java b/src/main/java/snackscription/review/service/ReviewService.java index bcd511a..64462a3 100644 --- a/src/main/java/snackscription/review/service/ReviewService.java +++ b/src/main/java/snackscription/review/service/ReviewService.java @@ -8,6 +8,7 @@ import snackscription.review.exception.InvalidStateException; import snackscription.review.exception.ReviewNotFoundException; import snackscription.review.model.Review; +import snackscription.review.model.ReviewId; import snackscription.review.model.ReviewState; import snackscription.review.repository.ReviewRepository; @@ -19,45 +20,35 @@ public ReviewService (ReviewRepository reviewRepository) { this.reviewRepository = reviewRepository; } - public Review findById(String reviewId) throws ReviewNotFoundException { - Optional oReview = reviewRepository.findById(reviewId); - - if (oReview.isEmpty()) { - throw new ReviewNotFoundException(); - } - - return oReview.get(); - } - - public List findBySubscriptionBoxId(String subscriptionBoxId) { - return reviewRepository.findBySubscriptionBoxId(subscriptionBoxId); - } - public Review createReview(int rating, String content, String subscriptionBoxId, String userId) throws Exception { - Review review = new Review(rating, content, userId, subscriptionBoxId); + Review review = new Review(rating, content, subscriptionBoxId, userId); reviewRepository.save(review); return review; } - public List getAllSubscriptionBoxReview(String subscriptionBoxId, String state) throws Exception { + public Review getReview(String subsbox, String user) throws Exception { + Optional oreview = reviewRepository.findById(new ReviewId(user, subsbox)); + if (oreview.isEmpty()) { + throw new ReviewNotFoundException(); + } + return oreview.get(); + } + + public List getSubsboxReview(String subscriptionBoxId, String state) throws Exception { if (state == null) { - return reviewRepository.findBySubscriptionBoxId(subscriptionBoxId); + return reviewRepository.findByIdSubsbox(subscriptionBoxId); } else { state = state.toUpperCase(); ReviewState reviewState = Enum.valueOf(ReviewState.class, state); if (reviewState == null) { throw new InvalidStateException(); } - return reviewRepository.findBySubscriptionBoxIdAndState(subscriptionBoxId, reviewState); + return reviewRepository.findByIdSubsboxAndState(subscriptionBoxId, reviewState); } } - public Review getReview(String subscriptionBoxId, String userId) throws Exception { - return reviewRepository.findBySubscriptionBoxIdAndUserId(subscriptionBoxId, userId); - } - public Review editReview(int rating, String content, String subscriptionBoxId, String userId) throws Exception { - Review review = reviewRepository.findBySubscriptionBoxIdAndUserId(subscriptionBoxId, userId); + Review review = reviewRepository.findByIdSubsboxAndIdAuthor(subscriptionBoxId, userId); if (review == null) { throw new ReviewNotFoundException(); @@ -69,25 +60,25 @@ public Review editReview(int rating, String content, String subscriptionBoxId, S return reviewRepository.save(review); } - public void deleteReview(String subscriptionBoxId, String userId) throws Exception { - Review review = reviewRepository.findBySubscriptionBoxIdAndUserId(subscriptionBoxId, userId); - - if (review == null) { - throw new ReviewNotFoundException(); - } - - reviewRepository.delete(review); - } - - public Review approveReview(String reviewId) throws Exception { - Review review = findById(reviewId); + public Review approveReview(String subsbox, String user) throws Exception { + Review review = getReview(subsbox, user); review.approve(); return reviewRepository.save(review); } - public Review rejectReview(String reviewId) throws Exception { - Review review = findById(reviewId); + public Review rejectReview(String subsbox, String user) throws Exception { + Review review = getReview(subsbox, user); review.reject(); return reviewRepository.save(review); } + + public void deleteReview(String subsbox, String user) throws Exception { + Review review = reviewRepository.findByIdSubsboxAndIdAuthor(subsbox, user); + + if (review == null) { + throw new ReviewNotFoundException(); + } + + reviewRepository.delete(review); + } } diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index dfdbb9f..083d035 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:postgresql://localhost:5432/snackscription_review +spring.datasource.url=jdbc:postgresql://localhost:5433/snackscription_review spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=create-drop diff --git a/src/test/java/snackscription/review/controller/ReviewControllerTest.java b/src/test/java/snackscription/review/controller/ReviewControllerTest.java index fa4260a..bf0ccca 100644 --- a/src/test/java/snackscription/review/controller/ReviewControllerTest.java +++ b/src/test/java/snackscription/review/controller/ReviewControllerTest.java @@ -4,7 +4,6 @@ import static org.mockito.Mockito.verify; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -12,29 +11,21 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.internal.stubbing.answers.DoesNothing; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -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.request.MockMvcRequestBuilders.put; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; -import org.springframework.test.web.servlet.result.JsonPathResultMatchers; import snackscription.review.model.Review; import snackscription.review.model.ReviewState; import snackscription.review.service.ReviewService; @@ -54,11 +45,11 @@ public class ReviewControllerTest { public void setUp() { this.reviews = new ArrayList<>(); - Review review1 = new Review(5, "I love it", "user_123", "subsbox_123"); - Review review2 = new Review(1, "I hate it", "user_124", "subsbox_123"); - Review review3 = new Review(2, "Hmmmm idk", "user_124", "subsbox_124"); - Review review4 = new Review(3, "It's okay", "user_125", "subsbox_124"); - Review review5 = new Review(4, "I like it", "user_126", "subsbox_124"); + Review review1 = new Review(5, "I love it", "subsbox_123", "user_123"); + Review review2 = new Review(1, "I hate it", "subsbox_123", "user_124"); + Review review3 = new Review(2, "Hmmmm idk", "subsbox_124", "user_124"); + Review review4 = new Review(3, "It's okay", "subsbox_124", "user_125"); + Review review5 = new Review(4, "I like it", "subsbox_124", "user_126"); review1.setState(ReviewState.PENDING); review4.setState(ReviewState.APPROVED); @@ -73,36 +64,36 @@ public void setUp() { } @Test - public void testCreateSubscriptionBoxReview() throws Exception{ + public void testCreateSubsboxReview() throws Exception{ Review review = reviews.getFirst(); - when(reviewService.createReview(review.getRating(), review.getContent(), review.getSubscriptionBoxId(), review.getUserId())).thenReturn(review); + when(reviewService.createReview(review.getRating(), review.getContent(), review.getId().getSubsbox(), review.getId().getAuthor())).thenReturn(review); - ResultActions result = mockMvc.perform(post("/subscription-boxes/{subscriptionBoxId}", review.getSubscriptionBoxId()) + ResultActions result = mockMvc.perform(post("/reviews/subscription-boxes/{subsbox}", review.getSubsbox()) .contentType(MediaType.APPLICATION_JSON) - .content("{\"rating\": 5, \"content\": \"I love it\", \"userId\": \"user_123\"}")) + .content("{\"rating\": 5, \"content\": \"I love it\", \"author\": \"user_123\"}")) .andExpect(status().isCreated()) .andExpect(jsonPath("$.rating", is(5))) .andExpect(jsonPath("$.content", is("I love it"))) - .andExpect(jsonPath("$.userId", is("user_123"))) - .andExpect(jsonPath("$.subscriptionBoxId", is("subsbox_123"))); + .andExpect(jsonPath("$.author", is("user_123"))) + .andExpect(jsonPath("$.subsbox", is("subsbox_123"))); - verify(reviewService).createReview(review.getRating(), review.getContent(), review.getSubscriptionBoxId(), review.getUserId()); + verify(reviewService).createReview(review.getRating(), review.getContent(), review.getSubsbox(), review.getAuthor()); } @Test public void testReadAllPublicSubscriptionBoxReview() throws Exception { List approvedReviews = new ArrayList<>(); - String subsboxId = "subsbox_124"; + String subsbox = "subsbox_124"; for (Review review : reviews) { - if (review.getSubscriptionBoxId().equals(subsboxId) && review.getState().equals(ReviewState.APPROVED)) { + if (review.getSubsbox().equals(subsbox) && review.getState().equals(ReviewState.APPROVED)) { approvedReviews.add(review); } } - when(reviewService.getAllSubscriptionBoxReview(subsboxId, "APPROVED")).thenReturn(approvedReviews); + when(reviewService.getSubsboxReview(subsbox, "APPROVED")).thenReturn(approvedReviews); - String result = mockMvc.perform(get("/subscription-boxes/{subscriptionBoxId}", subsboxId)) + String result = mockMvc.perform(get("/reviews/subscription-boxes/{subsbox}/public", subsbox)) .andExpect(status().isOk()) .andExpect(jsonPath("$", hasSize(approvedReviews.size()))) .andReturn() @@ -114,146 +105,138 @@ public void testReadAllPublicSubscriptionBoxReview() throws Exception { String prefixMatcher = String.format("$[%d]", i); int rating = JsonPath.read(result, prefixMatcher + ".rating"); String content = JsonPath.read(result, prefixMatcher + ".content"); - String userId = JsonPath.read(result, prefixMatcher + ".userId"); - String curSubscriptionBoxId = JsonPath.read(result, prefixMatcher + ".subscriptionBoxId"); + String author = JsonPath.read(result, prefixMatcher + ".author"); + String curSubscriptionBoxId = JsonPath.read(result, prefixMatcher + ".subsbox"); - Review review = new Review(rating, content, userId, curSubscriptionBoxId); + Review review = new Review(rating, content, curSubscriptionBoxId, author); foundReviews.add(review); } - Comparator cmp = new Comparator() { - @Override - public int compare(Review o1, Review o2) { - return o1.getUserId().compareTo(o2.getUserId()); - } - }; - + Comparator cmp = Comparator.comparing(Review::getAuthor); approvedReviews.sort(cmp); foundReviews.sort(cmp); for (int i=0; i curReviews = new ArrayList<>(); -// String subscriptionBoxId = this.reviews.getFirst().getSubscriptionBoxId(); +// String subscriptionBoxId = this.reviews.getFirst().getSubsbox(); // for (Review review : this.reviews) { -// if (review.getSubscriptionBoxId().equals(subscriptionBoxId)) { +// if (review.getSubsbox().equals(subscriptionBoxId)) { // curReviews.add(review); // } // } @@ -335,7 +318,7 @@ public void testRejectReview() throws Exception { // Comparator cmp = new Comparator() { // @Override // public int compare(Review o1, Review o2) { -// return o1.getUserId().compareTo(o2.getUserId()); +// return o1.getAuthor().compareTo(o2.getAuthor()); // } // }; @@ -345,8 +328,8 @@ public void testRejectReview() throws Exception { // for (int i=0; i(); - - Review review1 = new Review(5, "I love it", "user_123", "subsbox_123"); - Review review2 = new Review(1, "I hate it", "user_124", "subsbox_123"); - Review review3 = new Review(2, "Hmmmm idk", "user_124", "subsbox_124"); - Review review4 = new Review(3, "It's okay", "user_125", "subsbox_124"); - Review review5 = new Review(4, "I like it", "user_126", "subsbox_124"); + + Review review1 = new Review(5, "I love it", "subsbox_123", "user_123"); + Review review2 = new Review(1, "I hate it", "subsbox_123", "user_124"); + Review review3 = new Review(2, "Hmmmm idk", "subsbox_124", "user_124"); + Review review4 = new Review(3, "It's okay", "subsbox_124", "user_125"); + Review review5 = new Review(4, "I like it", "subsbox_124", "user_126"); review1.setState(ReviewState.PENDING); review4.setState(ReviewState.APPROVED); @@ -50,14 +49,14 @@ public void setUp() { public void testFindBySubscriptionBoxId() { List curReviews = new ArrayList<>(); - String subsbox_id = this.reviews.getFirst().getSubscriptionBoxId(); + String subsbox_id = this.reviews.getFirst().getSubsbox(); for (Review review : this.reviews) { - if (review.getSubscriptionBoxId().equals(subsbox_id)) { + if (review.getSubsbox().equals(subsbox_id)) { curReviews.add(review); } } - List foundReviews = reviewRepository.findBySubscriptionBoxId(subsbox_id); + List foundReviews = reviewRepository.findByIdSubsbox(subsbox_id); assertEquals(curReviews.size(), foundReviews.size()); for (int i=0; i curReviews = new ArrayList<>(); - String subsbox_id = this.reviews.getFirst().getSubscriptionBoxId(); + String subsbox_id = this.reviews.getFirst().getSubsbox(); for (Review review : this.reviews) { - if (review.getSubscriptionBoxId().equals(subsbox_id) && review.getState().equals(ReviewState.APPROVED)){ + if (review.getSubsbox().equals(subsbox_id) && review.getState().equals(ReviewState.APPROVED)){ curReviews.add(review); } } - List foundReviews = reviewRepository.findBySubscriptionBoxIdAndState(subsbox_id, ReviewState.APPROVED); + List foundReviews = reviewRepository.findByIdSubsboxAndState(subsbox_id, ReviewState.APPROVED); assertEquals(curReviews.size(), foundReviews.size()); for (int i=0; i reviews; - // @Test - // public void testGetAllSubscriptionBoxReview() { - // ReviewService reviewService = new ReviewService(reviewRepo); - - // Optional review = Optional.of(new Review( - // 5, "amazing", "user1", "subsboxId" - // )); - - // when(reviewRepo.findById("subsboxId")).thenReturn(review); - - // Review foundReview = reviewService.getAllSubscriptionBoxReview("subsboxId"); - - // assertEquals(review.get(), foundReview); - - // verify(reviewRepo).findBySubscriptionBoxId("subsboxId"); - - // } - @BeforeEach public void setUp() { reviewService = new ReviewService(reviewRepo); - Review review1 = new Review(5, "I love it", "user_123", "subsbox_123"); - Review review2 = new Review(1, "I hate it", "user_124", "subsbox_123"); - Review review3 = new Review(2, "Hmmmm idk", "user_124", "subsbox_124"); - Review review4 = new Review(3, "It's okay", "user_125", "subsbox_124"); - Review review5 = new Review(4, "I like it", "user_126", "subsbox_124"); + Review review1 = new Review(5, "I love it", "subsbox_123", "user_123"); + Review review2 = new Review(1, "I hate it", "subsbox_123", "user_124"); + Review review3 = new Review(2, "Hmmmm idk", "subsbox_124", "user_124"); + Review review4 = new Review(3, "It's okay", "subsbox_124", "user_125"); + Review review5 = new Review(4, "I like it", "subsbox_124", "user_126"); review1.setState(ReviewState.PENDING); review4.setState(ReviewState.APPROVED); @@ -78,61 +54,25 @@ public void setUp() { } @Test - public void getReviewById() throws Exception { - - - Optional review = Optional.of(new Review( - 5, "amazing", "user1", "subsboxId" - )); - - String reviewId = review.get().getId(); - - when(reviewRepo.findById(reviewId)).thenReturn(review); - - Review foundReview = reviewService.findById(reviewId); - - assertEquals(foundReview, review.get()); - - verify(reviewRepo).findById(reviewId); - } - - @Test - public void getReviewByIdNotFound() { - ReviewService reviewService = new ReviewService(reviewRepo); - - Optional review = Optional.empty(); - - String reviewId = "reviewId"; - - when(reviewRepo.findById(reviewId)).thenReturn(review); - - assertThrows(ReviewNotFoundException.class, () -> { - reviewService.findById(reviewId); - }); - - verify(reviewRepo).findById(reviewId); - } - - @Test - public void getReviewsBySubscriptionBoxId() { + public void getReviewsBySubscriptionBoxId() throws Exception { ReviewService reviewService = new ReviewService(reviewRepo); List curReviews = new ArrayList<>(); - String subscriptionBoxId = this.reviews.getFirst().getSubscriptionBoxId(); + String subscriptionBoxId = this.reviews.getFirst().getSubsbox(); for (Review review : this.reviews) { - if (review.getSubscriptionBoxId().equals(subscriptionBoxId)) { + if (review.getSubsbox().equals(subscriptionBoxId)) { curReviews.add(review); } } - when(reviewRepo.findBySubscriptionBoxId(subscriptionBoxId)).thenReturn(curReviews); + when(reviewRepo.findByIdSubsbox(subscriptionBoxId)).thenReturn(curReviews); - List foundReviews = reviewService.findBySubscriptionBoxId(subscriptionBoxId); + List foundReviews = reviewService.getSubsboxReview(subscriptionBoxId, null); assertEquals(curReviews, foundReviews); - verify(reviewRepo).findBySubscriptionBoxId(subscriptionBoxId); + verify(reviewRepo).findByIdSubsbox(subscriptionBoxId); } @Test @@ -144,8 +84,8 @@ public void testCreateReview() throws Exception { Review savedReview = reviewService.createReview( review.getRating(), review.getContent(), - review.getSubscriptionBoxId(), - review.getUserId()); + review.getSubsbox(), + review.getAuthor()); assertEqualReview(review, savedReview); @@ -153,84 +93,82 @@ public void testCreateReview() throws Exception { } @Test - public void testGetAllSubscriptionBoxReview() throws Exception { - String subscriptionBoxId = this.reviews.getFirst().getSubscriptionBoxId(); + public void testgetSubsboxReview() throws Exception { + String subscriptionBoxId = this.reviews.getFirst().getSubsbox(); List curReviews = new ArrayList<>(); for (Review review : this.reviews) { - if (review.getSubscriptionBoxId().equals(subscriptionBoxId)) { + if (review.getSubsbox().equals(subscriptionBoxId)) { curReviews.add(review); } } - when(reviewRepo.findBySubscriptionBoxId(subscriptionBoxId)).thenReturn(curReviews); + when(reviewRepo.findByIdSubsbox(subscriptionBoxId)).thenReturn(curReviews); - List foundReviews = reviewService.getAllSubscriptionBoxReview(subscriptionBoxId, null); + List foundReviews = reviewService.getSubsboxReview(subscriptionBoxId, null); assertEquals(curReviews, foundReviews); - verify(reviewRepo).findBySubscriptionBoxId(subscriptionBoxId); + verify(reviewRepo).findByIdSubsbox(subscriptionBoxId); } @Test - public void testGetAllSubscriptionBoxReviewApproved() throws Exception { - String subscriptionBoxId = this.reviews.getFirst().getSubscriptionBoxId(); + public void testgetSubsboxReviewApproved() throws Exception { + String subscriptionBoxId = this.reviews.getFirst().getSubsbox(); List cuReviews = new ArrayList<>(); for (Review review : this.reviews) { - if (review.getSubscriptionBoxId().equals(subscriptionBoxId) && review.getState().equals(ReviewState.APPROVED)) { + if (review.getSubsbox().equals(subscriptionBoxId) && review.getState().equals(ReviewState.APPROVED)) { cuReviews.add(review); } } - when(reviewRepo.findBySubscriptionBoxIdAndState(subscriptionBoxId, ReviewState.APPROVED)).thenReturn(cuReviews); + when(reviewRepo.findByIdSubsboxAndState(subscriptionBoxId, ReviewState.APPROVED)).thenReturn(cuReviews); - List foundReviews = reviewService.getAllSubscriptionBoxReview(subscriptionBoxId, "APPROVED"); + List foundReviews = reviewService.getSubsboxReview(subscriptionBoxId, "APPROVED"); assertEquals(cuReviews, foundReviews); - verify(reviewRepo).findBySubscriptionBoxIdAndState(subscriptionBoxId, ReviewState.APPROVED); + verify(reviewRepo).findByIdSubsboxAndState(subscriptionBoxId, ReviewState.APPROVED); } @Test public void testEditReview() throws Exception { Review review = reviews.getFirst(); - String subscriptionBoxId = review.getSubscriptionBoxId(); - String userId = review.getUserId(); + String subsbox = review.getSubsbox(); + String author = review.getAuthor(); int newRating = 1; String newContent = "Changed content"; - Review newReview = new Review(newRating, newContent, userId, subscriptionBoxId); + Review newReview = new Review(newRating, newContent, author, subsbox); newReview.setId(review.getId()); - when(reviewRepo.findBySubscriptionBoxIdAndUserId(subscriptionBoxId, userId)).thenReturn(review); + when(reviewRepo.findByIdSubsboxAndIdAuthor(subsbox, author)).thenReturn(review); when(reviewRepo.save(any(Review.class))).thenReturn(newReview); - Review editedReview = reviewService.editReview(newRating, newContent, subscriptionBoxId, userId); + Review editedReview = reviewService.editReview(newRating, newContent, subsbox, author); assertEquals(newRating, editedReview.getRating()); assertEquals(newContent, editedReview.getContent()); - assertEquals(subscriptionBoxId, editedReview.getSubscriptionBoxId()); - assertEquals(userId, editedReview.getUserId()); + assertEquals(subsbox, editedReview.getSubsbox()); + assertEquals(author, editedReview.getAuthor()); assertEquals(review.getId(), editedReview.getId()); } @Test public void testDeleteReview() throws Exception { - String subscriptionBoxId = this.reviews.getFirst().getSubscriptionBoxId(); - String userId = this.reviews.getFirst().getUserId(); + String subsbox = this.reviews.getFirst().getSubsbox(); + String author = this.reviews.getFirst().getAuthor(); Review review = reviews.getFirst(); - when(reviewRepo.findBySubscriptionBoxIdAndUserId(subscriptionBoxId, userId)).thenReturn(review); - - reviewService.deleteReview(subscriptionBoxId, userId); + when(reviewRepo.findByIdSubsboxAndIdAuthor(subsbox, author)).thenReturn(review); - when(reviewRepo.findBySubscriptionBoxIdAndUserId(subscriptionBoxId, userId)).thenReturn(null); + reviewService.deleteReview(subsbox, author); - assertNull(reviewService.getReview(subscriptionBoxId, userId)); + assertThrows(ReviewNotFoundException.class, () -> reviewService.getReview(subsbox, author)); verify(reviewRepo).delete(review); } @@ -238,8 +176,8 @@ public void testDeleteReview() throws Exception { public void assertEqualReview(Review review1, Review review2) { assertEquals(review1.getRating(), review2.getRating()); assertEquals(review1.getContent(), review2.getContent()); - assertEquals(review1.getUserId(), review2.getUserId()); - assertEquals(review1.getSubscriptionBoxId(), review2.getSubscriptionBoxId()); + assertEquals(review1.getAuthor(), review2.getAuthor()); + assertEquals(review1.getSubsbox(), review2.getSubsbox()); } } From a416bbc9e5a33fdae3baf4bf4358078c4e139186 Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Wed, 15 May 2024 15:35:17 +0700 Subject: [PATCH 4/7] [FIX] Change application properties --- src/main/resources/application-dev.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 083d035..dfdbb9f 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:postgresql://localhost:5433/snackscription_review +spring.datasource.url=jdbc:postgresql://localhost:5432/snackscription_review spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=create-drop From 032d5df9c612c72fc7be2713c6c143c5fb51164f Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Wed, 15 May 2024 15:57:21 +0700 Subject: [PATCH 5/7] [FIX] Fix ci-cd application properties --- .github/workflows/cd.yml | 2 ++ .github/workflows/ci.yml | 6 +++++- src/main/resources/application-dev.properties | 2 +- src/main/resources/application.properties | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ebdb71d..30e9471 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -70,6 +70,8 @@ jobs: ./gradlew check --info --stacktrace ./gradlew test ./gradlew jacocoTestReport + env: + PRODUCTION: test # (Optional) Add steps for generating coverage report and other post-test tasks publish: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dbb8fa..de84db9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,8 +70,10 @@ jobs: distribution: "temurin" java-version: "21" cache: "gradle" + - name: Make gradlew executable run: chmod +x ./gradlew + - name: Cache Gradle dependencies uses: actions/cache@v4 with: @@ -85,4 +87,6 @@ jobs: ./gradlew check --info --stacktrace ./gradlew test ./gradlew jacocoTestReport - # (Optional) Add steps for generating coverage report and other post-test tasks + env: + PRODUCTION: test + # (Optional) Add steps for generating coverage report and other post-test tasks diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index dfdbb9f..083d035 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:postgresql://localhost:5432/snackscription_review +spring.datasource.url=jdbc:postgresql://localhost:5433/snackscription_review spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.hibernate.ddl-auto=create-drop diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 338aaa3..1bd6102 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,4 @@ spring.application.name=review -spring.profiles.active=${PRODUCTION:dev} \ No newline at end of file +spring.profiles.active=${PRODUCTION:dev} +management.endpoint.info.enabled=true +management.endpoints.web.exposure.include=health,metrics,prometheus,loggers \ No newline at end of file From f45549cc5f2c121f0aaf998cbe0b73d8e6c0fca2 Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Wed, 15 May 2024 20:53:00 +0700 Subject: [PATCH 6/7] Add prometheus & grafana config --- Dockerfile | 3 +- build.gradle.kts | 2 + deployment.yaml | 22 +++++++++++ grafana-deployment.yaml | 30 +++++++++++++++ prometheus-development.yaml | 38 +++++++++++++++++++ prometheus.yml | 8 ++++ service.yaml | 11 ++++++ .../review/ReviewAppConfig.java | 14 +++++++ .../review/service/ReviewService.java | 5 +++ 9 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 deployment.yaml create mode 100644 grafana-deployment.yaml create mode 100644 prometheus-development.yaml create mode 100644 prometheus.yml create mode 100644 service.yaml create mode 100644 src/main/java/snackscription/review/ReviewAppConfig.java diff --git a/Dockerfile b/Dockerfile index db9b952..516b0e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,6 @@ ENV JDBC_DATABASE_URL ${JDBC_DATABASE_URL} ENV JDBC_DATABASE_USERNAME ${JDBC_DATABASE_USERNAME} WORKDIR /app -COPY ./review-0.0.1-SNAPSHOT.jar /app -RUN ls -la +COPY build/libs/review-0.0.1-SNAPSHOT.jar /app/review-0.0.1-SNAPSHOT.jar EXPOSE 8080 CMD ["java","-jar","review-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index c18a996..23ef6a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,6 +26,8 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-thymeleaf") implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("io.micrometer:micrometer-registry-prometheus") compileOnly("org.projectlombok:lombok") developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("org.postgresql:postgresql") diff --git a/deployment.yaml b/deployment.yaml new file mode 100644 index 0000000..a4401a5 --- /dev/null +++ b/deployment.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: snackscription-review-deployment +spec: + replicas: 3 + selector: + matchLabels: + app: snackscription-review + template: + metadata: + labels: + app: snackscription-review + spec: + containers: + - name: snackscription-review + image: asteriskzie/snackscription-review:latest + ports: + - containerPort: 8080 + env: + - name: PRODUCTION + value: prod \ No newline at end of file diff --git a/grafana-deployment.yaml b/grafana-deployment.yaml new file mode 100644 index 0000000..5034202 --- /dev/null +++ b/grafana-deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grafana +spec: + replicas: 1 + selector: + matchLabels: + app: grafana + template: + metadata: + labels: + app: grafana + spec: + containers: + - name: grafana + image: grafana/grafana + ports: + - containerPort: 3000 +--- +apiVersion: v1 +kind: Service +metadata: + name: grafana +spec: + type: LoadBalancer + ports: + - port: 3000 + selector: + app: grafana diff --git a/prometheus-development.yaml b/prometheus-development.yaml new file mode 100644 index 0000000..e49eabd --- /dev/null +++ b/prometheus-development.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prometheus +spec: + replicas: 1 + selector: + matchLabels: + app: prometheus + template: + metadata: + labels: + app: prometheus + spec: + containers: + - name: prometheus + image: prom/prometheus + ports: + - containerPort: 9090 + volumeMounts: + - name: prometheus-config + mountPath: /etc/prometheus/ + subPath: prometheus.yml + volumes: + - name: prometheus-config + configMap: + name: prometheus-config +--- +apiVersion: v1 +kind: Service +metadata: + name: prometheus +spec: + type: LoadBalancer + ports: + - port: 9090 + selector: + app: prometheus diff --git a/prometheus.yml b/prometheus.yml new file mode 100644 index 0000000..3ee0fde --- /dev/null +++ b/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'snackscription-review' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['snackscription-review-service:8080'] diff --git a/service.yaml b/service.yaml new file mode 100644 index 0000000..25b65be --- /dev/null +++ b/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: snackscription-review-service +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 8080 + selector: + app: snackscription-review diff --git a/src/main/java/snackscription/review/ReviewAppConfig.java b/src/main/java/snackscription/review/ReviewAppConfig.java new file mode 100644 index 0000000..e3519e7 --- /dev/null +++ b/src/main/java/snackscription/review/ReviewAppConfig.java @@ -0,0 +1,14 @@ +package snackscription.review; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ReviewAppConfig { + @Bean + public MeterRegistry getMeterRegistry() { + return new CompositeMeterRegistry(); + } +} diff --git a/src/main/java/snackscription/review/service/ReviewService.java b/src/main/java/snackscription/review/service/ReviewService.java index 64462a3..b665e00 100644 --- a/src/main/java/snackscription/review/service/ReviewService.java +++ b/src/main/java/snackscription/review/service/ReviewService.java @@ -2,7 +2,10 @@ import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import snackscription.review.exception.InvalidStateException; @@ -13,8 +16,10 @@ import snackscription.review.repository.ReviewRepository; @Service +@Component public class ReviewService { private ReviewRepository reviewRepository; + private AtomicInteger activeUsers = null; public ReviewService (ReviewRepository reviewRepository) { this.reviewRepository = reviewRepository; From eefe1046b45fa46251194a448763f10315f6ede5 Mon Sep 17 00:00:00 2001 From: asteriskzie Date: Sat, 18 May 2024 16:02:14 +0700 Subject: [PATCH 7/7] Add Prometheus and Grafana configuration --- .monitoring/docker-compose.yml | 19 ++++++++++ .../provisioning/datasources/datasources.yml | 7 ++++ .monitoring/prometheus/prometheus.yml | 8 ++++ application.yml | 5 +++ build.gradle.kts | 2 +- grafana-deployment.yaml | 30 --------------- prometheus-development.yaml | 38 ------------------- prometheus.yml | 8 ---- .../review/controller/ReviewController.java | 2 - .../review/service/ReviewService.java | 1 - 10 files changed, 40 insertions(+), 80 deletions(-) create mode 100644 .monitoring/docker-compose.yml create mode 100644 .monitoring/grafana/provisioning/datasources/datasources.yml create mode 100644 .monitoring/prometheus/prometheus.yml create mode 100644 application.yml delete mode 100644 grafana-deployment.yaml delete mode 100644 prometheus-development.yaml delete mode 100644 prometheus.yml diff --git a/.monitoring/docker-compose.yml b/.monitoring/docker-compose.yml new file mode 100644 index 0000000..87b2c31 --- /dev/null +++ b/.monitoring/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3.7' + +services: + prometheus: + image: prom/prometheus:v2.44.0 + container_name: prometheus + ports: + - "9090:9090" + volumes: + - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + + grafana: + image: grafana/grafana:9.5.2 + container_name: grafana + ports: + - "3000:3000" + restart: unless-stopped + volumes: + - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources diff --git a/.monitoring/grafana/provisioning/datasources/datasources.yml b/.monitoring/grafana/provisioning/datasources/datasources.yml new file mode 100644 index 0000000..8d9f9d8 --- /dev/null +++ b/.monitoring/grafana/provisioning/datasources/datasources.yml @@ -0,0 +1,7 @@ +apiVersion: 1 +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true \ No newline at end of file diff --git a/.monitoring/prometheus/prometheus.yml b/.monitoring/prometheus/prometheus.yml new file mode 100644 index 0000000..94f2cfe --- /dev/null +++ b/.monitoring/prometheus/prometheus.yml @@ -0,0 +1,8 @@ +scrape_configs: + - job_name: 'MyAppMetrics' + metrics_path: '/actuator/prometheus' + scrape_interval: 3s + static_configs: + - targets: ['host.docker.internal:8080'] + labels: + application: 'Snackscription Review' \ No newline at end of file diff --git a/application.yml b/application.yml new file mode 100644 index 0000000..3feefab --- /dev/null +++ b/application.yml @@ -0,0 +1,5 @@ +management: + endpoints: + web: + exposure: + include: [ "prometheus" ] diff --git a/build.gradle.kts b/build.gradle.kts index 23ef6a4..01cc3e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,10 +27,10 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-thymeleaf") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-actuator") - implementation("io.micrometer:micrometer-registry-prometheus") compileOnly("org.projectlombok:lombok") developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("org.postgresql:postgresql") + runtimeOnly("io.micrometer:micrometer-registry-prometheus") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") annotationProcessor("org.projectlombok:lombok") testImplementation("org.springframework.boot:spring-boot-starter-test") diff --git a/grafana-deployment.yaml b/grafana-deployment.yaml deleted file mode 100644 index 5034202..0000000 --- a/grafana-deployment.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: grafana -spec: - replicas: 1 - selector: - matchLabels: - app: grafana - template: - metadata: - labels: - app: grafana - spec: - containers: - - name: grafana - image: grafana/grafana - ports: - - containerPort: 3000 ---- -apiVersion: v1 -kind: Service -metadata: - name: grafana -spec: - type: LoadBalancer - ports: - - port: 3000 - selector: - app: grafana diff --git a/prometheus-development.yaml b/prometheus-development.yaml deleted file mode 100644 index e49eabd..0000000 --- a/prometheus-development.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: prometheus -spec: - replicas: 1 - selector: - matchLabels: - app: prometheus - template: - metadata: - labels: - app: prometheus - spec: - containers: - - name: prometheus - image: prom/prometheus - ports: - - containerPort: 9090 - volumeMounts: - - name: prometheus-config - mountPath: /etc/prometheus/ - subPath: prometheus.yml - volumes: - - name: prometheus-config - configMap: - name: prometheus-config ---- -apiVersion: v1 -kind: Service -metadata: - name: prometheus -spec: - type: LoadBalancer - ports: - - port: 9090 - selector: - app: prometheus diff --git a/prometheus.yml b/prometheus.yml deleted file mode 100644 index 3ee0fde..0000000 --- a/prometheus.yml +++ /dev/null @@ -1,8 +0,0 @@ -global: - scrape_interval: 15s - -scrape_configs: - - job_name: 'snackscription-review' - metrics_path: '/actuator/prometheus' - static_configs: - - targets: ['snackscription-review-service:8080'] diff --git a/src/main/java/snackscription/review/controller/ReviewController.java b/src/main/java/snackscription/review/controller/ReviewController.java index d126faa..bd802cd 100644 --- a/src/main/java/snackscription/review/controller/ReviewController.java +++ b/src/main/java/snackscription/review/controller/ReviewController.java @@ -13,8 +13,6 @@ @RestController @RequestMapping("/reviews") public class ReviewController { - - private ReviewService reviewService; public ReviewController(ReviewService reviewService) { diff --git a/src/main/java/snackscription/review/service/ReviewService.java b/src/main/java/snackscription/review/service/ReviewService.java index b665e00..31a94c5 100644 --- a/src/main/java/snackscription/review/service/ReviewService.java +++ b/src/main/java/snackscription/review/service/ReviewService.java @@ -19,7 +19,6 @@ @Component public class ReviewService { private ReviewRepository reviewRepository; - private AtomicInteger activeUsers = null; public ReviewService (ReviewRepository reviewRepository) { this.reviewRepository = reviewRepository;