diff --git a/src/main/java/sg/com/smartinventory/controllers/CustomerController.java b/src/main/java/sg/com/smartinventory/controllers/CustomerController.java index 19ed27c..3c0735c 100644 --- a/src/main/java/sg/com/smartinventory/controllers/CustomerController.java +++ b/src/main/java/sg/com/smartinventory/controllers/CustomerController.java @@ -15,6 +15,7 @@ import jakarta.validation.Valid; import sg.com.smartinventory.entities.Customer; +import sg.com.smartinventory.entities.Review; // import sg.com.smartinventory.entities.Review; import sg.com.smartinventory.services.CustomerService; @@ -45,6 +46,13 @@ public ResponseEntity createCustomer(@Valid @RequestBody Customer cust return new ResponseEntity<>(newCustomer, HttpStatus.CREATED); } + @PostMapping("/{id}/reviews") + public ResponseEntity addReviewToCustomer(@PathVariable long id, @RequestBody Review review) { + // TODO: process POST request + Review newReview = customerService.addReviewToCustomer(id, review); + return new ResponseEntity<>(newReview, HttpStatus.OK); + } + // READ (all) @GetMapping("") public ResponseEntity> getAllCustomers() { @@ -59,10 +67,16 @@ public ResponseEntity getCustomer(@PathVariable long id) { return new ResponseEntity<>(foundCustomer, HttpStatus.OK); } + // READ (by name CONTAINS) + @GetMapping("/search") + public ResponseEntity> searchCustomer(@RequestParam String firstName) { + ArrayList customers = customerService.searchCustomer(firstName); + return new ResponseEntity<>(customers, HttpStatus.OK); + } + // UPDATE @PutMapping("/{id}") public ResponseEntity updateCustomer(@PathVariable long id, @RequestBody Customer customer) { - // TODO: process PUT request Customer updatedCustomer = customerService.updateCustomer(id, customer); return new ResponseEntity<>(updatedCustomer, HttpStatus.OK); } @@ -71,7 +85,7 @@ public ResponseEntity updateCustomer(@PathVariable long id, @RequestBo @DeleteMapping("/{id}") public ResponseEntity deleteCustomer(@PathVariable long id) { customerService.deleteCustomer(id); - return new ResponseEntity<>(HttpStatus.NOT_FOUND); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } // // Nested route - Add review to customer. diff --git a/src/main/java/sg/com/smartinventory/entities/Customer.java b/src/main/java/sg/com/smartinventory/entities/Customer.java index 1e20a2b..4e1a5bb 100644 --- a/src/main/java/sg/com/smartinventory/entities/Customer.java +++ b/src/main/java/sg/com/smartinventory/entities/Customer.java @@ -60,25 +60,15 @@ public class Customer { @Column(name = "email") private String email; - // Uni-directional One to Many mapping -> One Customer (Parent) can have many - // Reviews (Child). The extra column 'customer_id' will be created on the many - // side of the relationship, that is, in the Review table. The cascade attribute - // is set to CascadeType.ALL to cascade all operations (e.g., save, update, - // delete) to the associated Review entities. The @JoinColumn annotation is used - // to specify the foreign key column (customer_id) in the Review table that - // establishes the relationship. Thus, it specifies the foreign key column in - // the child entity’s table that refers to the parent entity. The ‘mappedBy’ - // attribute is not used in a unidirectional relationship as it’s specific to - // bidirectional relationships. - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - @JoinColumn(name = "customer_id", referencedColumnName = "id") + @OneToMany(mappedBy = "customer") private List reviews; public Customer() { } // Define Constructor for DataLoader. - public Customer(String firstName, String lastName, String country, String address, int postalCode, int phoneNumber, + public Customer(String firstName, String lastName, String country, String address, int postalCode, + int phoneNumber, String email) { this(); diff --git a/src/main/java/sg/com/smartinventory/entities/Review.java b/src/main/java/sg/com/smartinventory/entities/Review.java index 6d38719..0b44247 100644 --- a/src/main/java/sg/com/smartinventory/entities/Review.java +++ b/src/main/java/sg/com/smartinventory/entities/Review.java @@ -1,10 +1,13 @@ package sg.com.smartinventory.entities; +import com.fasterxml.jackson.annotation.JsonBackReference; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.Min; @@ -35,14 +38,19 @@ public class Review { @Column(name = "rating") private int rating; - @Min(value = 1, message = "Customer ID should start from 1. ") - @Column(name = "customer_id") - private long customerId; // FK. PostgreSQL bigserial data type. - @Min(value = 1, message = "Product ID should start from 1. ") @Column(name = "product_id") private long productId; // FK. PostgreSQL bigserial data type. + // @ManyToOne Customer -> Many Reviews can be linked to 1 Customer + // @Min(value = 1, message = "Customer ID should start from 1. ") + @JsonBackReference + @ManyToOne(optional = false) + @JoinColumn(name = "customer_id", referencedColumnName = "id") + private Customer customer; + + // @ManyToOne Product -> Many Reviews can be linked to 1 Product + public Review() { } @@ -53,11 +61,8 @@ public Review(String category, String reviewContent, int rating, long customerId this.category = category; this.reviewContent = reviewContent; this.rating = rating; - this.customerId = customerId; + // this.customerId = customerId; this.productId = productId; } - // @ManyToOne Product -> Many Reviews can be linked to 1 Product - - // @ManyToOne Customer -> Many Reviews can be linked to 1 Customer } \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/repositories/CustomerRepository.java b/src/main/java/sg/com/smartinventory/repositories/CustomerRepository.java index 50c89f1..7c4a062 100644 --- a/src/main/java/sg/com/smartinventory/repositories/CustomerRepository.java +++ b/src/main/java/sg/com/smartinventory/repositories/CustomerRepository.java @@ -14,7 +14,7 @@ public interface CustomerRepository extends JpaRepository { // List findById(long id); // Custom query to find all customers with a certain first name. - List findByFirstName(String firstName); + List findByFirstNameContaining(String firstName); // Custom query to find all customers with a certain last name. List findByLastName(String lastName); diff --git a/src/main/java/sg/com/smartinventory/serviceImpls/CustomerServiceImpl.java b/src/main/java/sg/com/smartinventory/serviceImpls/CustomerServiceImpl.java index b6b2f15..9e5c903 100644 --- a/src/main/java/sg/com/smartinventory/serviceImpls/CustomerServiceImpl.java +++ b/src/main/java/sg/com/smartinventory/serviceImpls/CustomerServiceImpl.java @@ -6,35 +6,41 @@ import org.springframework.stereotype.Service; import sg.com.smartinventory.entities.Customer; +import sg.com.smartinventory.entities.Review; // import sg.com.smartinventory.entities.Review; import sg.com.smartinventory.exceptions.CustomerNotFoundException; import sg.com.smartinventory.repositories.CustomerRepository; +import sg.com.smartinventory.repositories.ReviewRepository; import sg.com.smartinventory.services.CustomerService; @Service public class CustomerServiceImpl implements CustomerService { private CustomerRepository customerRepository; + private ReviewRepository reviewRepository; // @Autowired - public CustomerServiceImpl(CustomerRepository customerRepository) { + public CustomerServiceImpl(CustomerRepository customerRepository, ReviewRepository reviewRepository) { this.customerRepository = customerRepository; + this.reviewRepository = reviewRepository; } + // - - - POST METHODS @Override public Customer createCustomer(Customer customer) { Customer newCustomer = customerRepository.save(customer); - return newCustomer; } + public Review addReviewToCustomer(long id, Review review) { + Customer customer = customerRepository.findById(id).orElseThrow(() -> new CustomerNotFoundException(id)); + review.setCustomer(customer); + Review newReview = reviewRepository.save(review); + return newReview; + } + + // - - - GET METHODS @Override public Customer getCustomer(Long id) { - // Optional optionalCustomer = customerRepository.findById(id); - // if(optionalCustomer.isPresent()) { - // Customer foundCustomer = optionalCustomer.get(); - // return foundCustomer; - // } - // throw new CustomerNotFoundException(id); return customerRepository.findById(id).orElseThrow(() -> new CustomerNotFoundException(id)); } @@ -44,6 +50,14 @@ public ArrayList getAllCustomers() { return (ArrayList) allCustomers; } + // searches for first name that contains search term + @Override + public ArrayList searchCustomer(String firstName) { + List customers = customerRepository.findByFirstNameContaining(firstName); + return (ArrayList) customers; + } + + // - - - PUT METHODS @Override public Customer updateCustomer(Long id, Customer customer) { // Retrieve the customer from the database. @@ -63,8 +77,13 @@ public Customer updateCustomer(Long id, Customer customer) { return customerRepository.save(customerToUpdate); } + // - - - DELETE METHODS @Override public void deleteCustomer(long id) { + // findById method allows deleteCustomer to throw exception if no id of + // such type is avail to be deleted i.e response will be 404 (Expected) instead + // of 204, to tell client that resource not found + customerRepository.findById(id).orElseThrow(() -> new CustomerNotFoundException(id)); customerRepository.deleteById(id); } diff --git a/src/main/java/sg/com/smartinventory/serviceImpls/ReviewServiceImpl.java b/src/main/java/sg/com/smartinventory/serviceImpls/ReviewServiceImpl.java index 8c51149..9d4d736 100644 --- a/src/main/java/sg/com/smartinventory/serviceImpls/ReviewServiceImpl.java +++ b/src/main/java/sg/com/smartinventory/serviceImpls/ReviewServiceImpl.java @@ -52,7 +52,6 @@ public Review updateReview(Long id, Review review) { reviewToUpdate.setCategory(review.getCategory()); reviewToUpdate.setReviewContent(review.getReviewContent()); reviewToUpdate.setRating(review.getRating()); - reviewToUpdate.setCustomerId(review.getCustomerId()); reviewToUpdate.setProductId(review.getProductId()); // Save the updated review back to the database. diff --git a/src/main/java/sg/com/smartinventory/services/CustomerService.java b/src/main/java/sg/com/smartinventory/services/CustomerService.java index 60d536c..59bff4a 100644 --- a/src/main/java/sg/com/smartinventory/services/CustomerService.java +++ b/src/main/java/sg/com/smartinventory/services/CustomerService.java @@ -4,12 +4,17 @@ import sg.com.smartinventory.entities.Customer; // import sg.com.smartinventory.entities.Review; +import sg.com.smartinventory.entities.Review; public interface CustomerService { Customer createCustomer(Customer customer); + Review addReviewToCustomer(long id, Review review); + Customer getCustomer(Long id); + ArrayList searchCustomer(String name); + ArrayList getAllCustomers(); Customer updateCustomer(Long id, Customer customer); diff --git a/src/main/java/sg/com/smartinventory/utility/DataLoader.java b/src/main/java/sg/com/smartinventory/utility/DataLoader.java index 38493b7..3c96d71 100644 --- a/src/main/java/sg/com/smartinventory/utility/DataLoader.java +++ b/src/main/java/sg/com/smartinventory/utility/DataLoader.java @@ -57,25 +57,38 @@ public void loadData() { productRepository.save(new Product("Books", "Science Fiction Novel", "Bestselling sci-fi novel set in a dystopian future. ", 14.99, 300)); - reviewRepository.save(new Review("Books", "Expected more from the ending, felt rushed. ", - 3, 1, 1)); - reviewRepository.save(new Review("Electronics", "Fast delivery, product works as expected. ", - 4, 1, 2)); - reviewRepository.save(new Review("Clothing", "Very comfortable t-shirt, fits perfectly. ", - 4, 2, 2)); - reviewRepository.save(new Review("Home & Kitchen", "Difficult to assemble, but sturdy once done. ", - 3, 2, 4)); - reviewRepository.save(new Review("Electronics", "Great smartphone with excellent features. ", - 5, 3, 1)); - reviewRepository.save(new Review("Clothing", "The color faded after a few washes. ", - 2, 3, 3)); - reviewRepository.save(new Review("Beauty", "Lovely fragrance, long-lasting. ", - 5, 4, 4)); - reviewRepository.save(new Review("Home & Kitchen", "Makes delicious coffee, easy to use. ", - 4, 4, 3)); - reviewRepository.save(new Review("Books", "Intriguing plot, couldn't put it down. ", - 5, 5, 5)); - reviewRepository.save(new Review("Beauty", "Disappointed with the scent, doesn't last long. ", - 2, 5, 5)); + // - - - NOTE: DATALOADER DONT WORK FOR ENTITIES WITH FK* (cant specify + // customer_id) - - - + + // reviewRepository.save(new Review("Books", "Expected more from the ending, + // felt rushed. ", + // 3, 1, 1)); + // reviewRepository.save(new Review("Electronics", "Fast delivery, product works + // as expected. ", + // 4, 1, 2)); + // reviewRepository.save(new Review("Clothing", "Very comfortable t-shirt, fits + // perfectly. ", + // 4, 2, 2)); + // reviewRepository.save(new Review("Home & Kitchen", "Difficult to assemble, + // but sturdy once done. ", + // 3, 2, 4)); + // reviewRepository.save(new Review("Electronics", "Great smartphone with + // excellent features. ", + // 5, 3, 1)); + // reviewRepository.save(new Review("Clothing", "The color faded after a few + // washes. ", + // 2, 3, 3)); + // reviewRepository.save(new Review("Beauty", "Lovely fragrance, long-lasting. + // ", + // 5, 4, 4)); + // reviewRepository.save(new Review("Home & Kitchen", "Makes delicious coffee, + // easy to use. ", + // 4, 4, 3)); + // reviewRepository.save(new Review("Books", "Intriguing plot, couldn't put it + // down. ", + // 5, 5, 5)); + // reviewRepository.save(new Review("Beauty", "Disappointed with the scent, + // doesn't last long. ", + // 2, 5, 5)); } } \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java b/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java index 92c140f..621aa6a 100644 --- a/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java +++ b/src/test/java/sg/com/smartinventory/controllers/ReviewControllerTest.java @@ -64,7 +64,7 @@ public void createReviewTest() throws Exception { // Step 1: Create a Review object Review newReview = Review.builder().category("Electronics") - .reviewContent("Great smartphone with excellent features. ").rating(5).customerId(1) + .reviewContent("Great smartphone with excellent features. ").rating(5) .productId(2).build(); // Step 2: Convert the Java object to JSON using ObjectMapper. diff --git a/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java b/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java index 6d85aac..9a0d44e 100644 --- a/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java +++ b/src/test/java/sg/com/smartinventory/services/ReviewServiceImplTest.java @@ -56,7 +56,7 @@ public void createReviewTest() { // 1. SETUP // Create a new review. Review review = Review.builder().category("Electronics") - .reviewContent("Great smartphone with excellent features. ").rating(5).customerId(1) + .reviewContent("Great smartphone with excellent features. ").rating(5) .productId(2).build(); // Mock the save method of the review repository.