Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #5

Merged
merged 22 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b0a3fd1
[REFACTOR] Add Spring Boot JPA and PostgreSQL dependencies, and updat…
asteriskzie Apr 24, 2024
30081f5
Merge pull request #3 from ADPRO-C11/setup-db
asteriskzie Apr 24, 2024
5427ada
Revert "[REFACTOR] Add Spring Boot JPA and PostgreSQL dependencies, a…
asteriskzie Apr 24, 2024
6ec68aa
Configure database (#6)
asteriskzie Apr 25, 2024
8df7270
Exploratory coding for Controller-Service-Repository
asteriskzie Apr 25, 2024
63aab45
[RED] Add tests for Review Repository
asteriskzie Apr 30, 2024
5367a15
[GREEN] Implement Review Repository
asteriskzie Apr 30, 2024
6ee44ee
[RED] Add test for Review Repository delete operation
asteriskzie Apr 30, 2024
ca783b4
[GREEN] Implement Review Repository delete operation
asteriskzie Apr 30, 2024
41621dc
[RED] Add test for review service
asteriskzie Apr 30, 2024
8f69454
[GREEN] Implement review service
asteriskzie Apr 30, 2024
040c525
[RED] Add controller test for get reviews by subscription box id
asteriskzie Apr 30, 2024
4508dd5
[GREEN] Implement controller for get reviews by subscription box id
asteriskzie Apr 30, 2024
87920c8
Fix: update state implementation
asteriskzie Apr 30, 2024
e08138b
[RED] Add repository test find by subscription box and state
asteriskzie May 1, 2024
7d5698d
[GREEN] Implement repository find by subscription box id and state
asteriskzie May 1, 2024
d7e6adf
[RED] Add review service test
asteriskzie May 1, 2024
40d1a72
[GREEN] Implement review service functionality
asteriskzie May 1, 2024
6e903f7
[RED] Add test for review controller
asteriskzie May 1, 2024
45d8f77
[GREEN] Implement review controller
asteriskzie May 1, 2024
a9ea2c1
[FIX] Fix controller test and implementation
asteriskzie May 1, 2024
fb3c52e
Merge pull request #7 from ADPRO-C11/basic-crud
asteriskzie May 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev
workflow_dispatch:

jobs:
Expand Down Expand Up @@ -42,12 +44,22 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: java-app
path: build/libs/*.jar
path: build/libs/*.jar

test:
name: Test
runs-on: ubuntu-latest
needs: build
needs: build
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
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ repositories {
}

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")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.postgresql:postgresql")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
Expand Down
133 changes: 132 additions & 1 deletion src/main/java/snackscription/review/controller/ReviewController.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,146 @@
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 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;



@Controller

@RestController
@RequestMapping("/")
public class ReviewController {


private ReviewService reviewService;

public ReviewController(ReviewService reviewService) {
this.reviewService = reviewService;
}

@GetMapping("")
public ResponseEntity<String> reviewPage() {
return ResponseEntity.ok().body("Welcome to the review service!");
}

@PostMapping("/api/subscription-boxes/{subscriptionBoxId}")
public ResponseEntity<Review> createSubscriptionBoxReview(@RequestBody Map<String,String> body, @PathVariable String subscriptionBoxId) {

try {
String userId = body.get("userId");
int rating = Integer.parseInt(body.get("rating"));
String content = body.get("content");

Review review = reviewService.createReview(rating, content, subscriptionBoxId, userId);
return new ResponseEntity<>(review, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}

@GetMapping("/api/subscription-boxes/{subscriptionBoxId}")
public ResponseEntity<List<Review>> getAllPublicSubscriptionBoxReview(@PathVariable String subscriptionBoxId) {
try {
List<Review> reviews = reviewService.getAllSubscriptionBoxReview(subscriptionBoxId, "APPROVED");
return new ResponseEntity<>(reviews, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}

@GetMapping("/api/subscription-boxes/{subscriptionBoxId}/users/self")
public ResponseEntity<Review> getSelfSubscriptionBoxReview(@RequestBody Map<String,String> body, @PathVariable String subscriptionBoxId) {
try {
String userId = body.get("userId");
Review review = reviewService.getReview(subscriptionBoxId, userId);
return new ResponseEntity<>(review, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}

@PutMapping("/api/subscription-boxes/{subscriptionBoxId}/users/self")
public ResponseEntity<Review> editSelfSubscriptionBoxId(@RequestBody Map<String,String> body, @PathVariable String subscriptionBoxId) {
try {
String userId = body.get("userId");
int rating = Integer.parseInt(body.get("rating"));
String content = body.get("content");

Review review = reviewService.editReview(rating, content, subscriptionBoxId, userId);
return new ResponseEntity<>(review, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}

@DeleteMapping("/api/subscription-boxes/{subscriptionBoxId}/users/self")
public ResponseEntity<Review> deleteSelfSubscriptionBoxReview(@RequestBody Map<String,String> 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);
}
}

@DeleteMapping("/api/subscription-boxes/{subscriptionBoxId}/users/{userId}")
public ResponseEntity<Review> deleteSubscriptionBoxReview(@PathVariable String subscriptionBoxId, @PathVariable String userId) {
try {
reviewService.deleteReview(subscriptionBoxId, userId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}

@GetMapping("/api/reviews/{subsboxId}")
public List<Review> getBySubscriptionBoxId(@PathVariable String subsboxId) throws Exception {
return reviewService.getAllSubscriptionBoxReview(subsboxId, null);
}

@GetMapping("/api/reviews/{reviewId}")
public Review getById(@PathVariable String reviewId) throws Exception {
return reviewService.findById(reviewId);
}

@PutMapping("/api/reviews/{reviewId}/approve")
public ResponseEntity<Review> approveReview(@PathVariable String reviewId) {
try {
Review review = reviewService.approveReview(reviewId);
return new ResponseEntity<>(review, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}

@PutMapping("/api/reviews/{reviewId}/reject")
public ResponseEntity<Review> rejectReview(@PathVariable String reviewId) {
try {
Review review = reviewService.rejectReview(reviewId);
return new ResponseEntity<>(review, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package snackscription.review.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import snackscription.review.model.Review;

@ControllerAdvice
public class ControllerAdvisor extends ResponseEntityExceptionHandler {
@ExceptionHandler(ReviewNotFoundException.class)
public ResponseEntity<Review> handleReviewNotFound(ReviewNotFoundException exc, WebRequest req) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

@ExceptionHandler(InvalidStateException.class)
public ResponseEntity<Review> handleInvalidState(InvalidStateException exc, WebRequest req) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package snackscription.review.exception;

public class InvalidStateException extends Exception {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package snackscription.review.exception;

public class ReviewNotFoundException extends Exception {

}
37 changes: 31 additions & 6 deletions src/main/java/snackscription/review/model/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,40 @@
import lombok.Setter;

import java.util.UUID;
import jakarta.persistence.*;


@Getter
@Setter
@Entity
@Table(name = "review")
public class Review {
private final String id;
@Id
private String id;

@Column(name = "rating", nullable = false)
private int rating;
@Setter

@Column(name = "content", nullable = false)
private String content;
@Setter

@Column(name = "state", nullable = false)
private ReviewState state;
private final String userId;
private final String subscriptionBoxId;

@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();
this.rating = rating;
this.content = content;
this.state = new ReviewStatePending(this);
this.state = ReviewState.PENDING;
this.userId = userId;
this.subscriptionBoxId = subscriptionBoxId;
}
Expand All @@ -36,4 +53,12 @@ public void setRating(int rating) {
}
this.rating = rating;
}

public void approve() {
this.state.approve(this);
}

public void reject() {
this.state.reject(this);
}
}
60 changes: 52 additions & 8 deletions src/main/java/snackscription/review/model/ReviewState.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,57 @@
package snackscription.review.model;

public abstract class ReviewState {
Review review;
String state;
ReviewState(Review review) {
this.review = review;
public enum ReviewState {
PENDING(new PendingState()),
APPROVED(new ApprovedState()),
REJECTED(new RejectedState());
private final StateTransition state;
private ReviewState(StateTransition state) {
this.state = state;
}

public abstract void approve();
void approve(Review review) {
state.approve(review);
}
void reject(Review review) {
state.reject(review);
}

private interface StateTransition {
void approve(Review review);
void reject(Review review);
}

private static class PendingState implements StateTransition {
@Override
public void approve(Review review) {
review.setState(APPROVED);
}

@Override
public void reject(Review review) {
review.setState(REJECTED);
}
}

private static class ApprovedState implements StateTransition {
@Override
public void approve(Review review) {
throw new RuntimeException("Review already approved.");
}

@Override public void reject(Review review) {
review.setState(REJECTED);
}
}

private static class RejectedState implements StateTransition {
@Override
public void approve(Review review) {
review.setState(APPROVED);
}

public abstract void reject();
}
@Override public void reject(Review review) {
throw new RuntimeException("Review already rejected.");
}
}
}
23 changes: 0 additions & 23 deletions src/main/java/snackscription/review/model/ReviewStateApproved.java

This file was deleted.

Loading