An allergy-specific restaurant review API built to solidfy the fundamentals of Java and Spring
RESTful Dining Review API with data persistence built in Java using Spring and Spring Data JPA with Hibernate.
This sample Dining Review API is designed for folks with allergies. Users indicate interest in any or all of three categories - peanut, egg, dairy.
Restaurants are rated in each category and have an overall rating which is the average of all review scores. Reviews contain username and optional scores for peanut, egg, dairy, as well as an optional commentary.
You will need an Integrated Develepor Environment (IDE) such as IntelliJ IDE, or VS Code and a Java Development Kit (JDK) version 8 or higher. From the command line:
git clone git@github.com:Meekb/Java_DiningReviews.git <project name>
cd <project name>
npm install
Open project in IDE, run local server to curl endpoints
The Model consists of four classes - User, Restaurant, DiningReview, and AdminReviewStatus.
- User, Restaurant, and DingingReview classes have an auto-generated id, date type Long
@Entity
@Table(name = "REVIEWS")
public @Data
@NoArgsConstructor
class DiningReview {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "USERNAME")
private String username;
@Column(name = "RESTAURANT")
private Long restaurant; // represented by the restaurant id
@Column(name = "PEANUTSCORE")
private Integer peanutScore;
@Column(name = "EGGSCORE")
private Integer eggScore;
@Column(name = "DAIRYSCORE")
private Integer dairyScore;
@Column(name = "COMMENTARY")
private String commentary;
@Column(name = "REVIEW_STATUS")
@Enumerated(EnumType.STRING)
private AdminReviewStatus adminReviewStatus;
}
AdminReviewStatus
public enum AdminReviewStatus {
PENDING,
APPROVED,
REJECTED,
}
Three controllers - UserController, RestaurantController, DiningReviewController
// creates / saves a new user
@PostMapping("/addNew")
public User createUser(@RequestBody User user) {
if (userRepository.getByUsername(user.getUsername()) != null && userRepository.findById(user.getId()).isPresent()) {
System.out.print("\nUser already exists");
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "User already exists");
}
if (userRepository.getByUsername(user.getUsername()) != null) {
System.out.print("\nUsername not available");
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Username not available");
}
return userRepository.save(user);
}
// returns users with peanut allergy interest
@GetMapping("/peanut_allergy")
public Iterable <User> findByPeanutInterestTrue() {
return this.userRepository.getByPeanutInterestTrue();
}
// returns restaurant Optional or ResponseStatusException
@GetMapping("/name_{name}")
public Optional<Restaurant> getRestaurantByName(@PathVariable("name") String name) {
Optional<Restaurant> restaurantOptional = this.restaurantRepository.findByNameContaining(name);
if (restaurantOptional.isEmpty()) {
System.out.print("No Restaurants found by that name.");
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "No Restaurants found with that name.");
} else return restaurantOptional;
}
// creates and saves a new restaurant
@PostMapping("/addNew")
public Restaurant createNewRestaurant(@RequestBody Restaurant restaurant) {
if (restaurantRepository.findByNameContaining(restaurant.getName()).isEmpty()) {
Restaurant newRestaurant = restaurantRepository.save(restaurant);
System.out.print("\nNew restaurant created! id: " + newRestaurant.getId() + ", name: " + newRestaurant.getName());
return newRestaurant;
} else throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Restaurant may already exist...");
}
// returns List of restaurants with overall rating greater than or equal to requested
@GetMapping("/overallRating_{overallRating}")
public List<Restaurant> getByOverallGreaterThanEqual(@PathVariable("overallRating") Double overallRating) {
List<Restaurant> restaurantsToReturn = this.restaurantRepository.findByOverallRatingGreaterThanEqual(overallRating);
if (restaurantsToReturn.isEmpty()) {
System.out.print("\nSorry, no restaurants found with that overall rating.");
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Sorry, no restaurants found with that overall rating.");
} else return restaurantsToReturn;
}
// returns review list with requested min dairy score
@GetMapping("/dairyScore_{dairyScore}")
public Iterable<DiningReview> getReviewsByDairyScoreGreaterThanEqual(@PathVariable("dairyScore") Integer dairyScore) {
Iterable<DiningReview> resultList = diningReviewRepository.findByDairyScoreGreaterThanEqual(dairyScore);
if (resultList != null) {
return diningReviewRepository.findByDairyScoreGreaterThanEqual(dairyScore);
} else return new ArrayList<>();
}
// creates and saves and new DiningReview with ResponseStatusExceptions for invalid user and restaurant id
@PostMapping("/addNew")
public DiningReview createNewDiningReview(@RequestBody DiningReview diningReview) {
if (restaurantRepository.findById(diningReview.getRestaurant()).isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Restaurant does not exist");
}
if (userRepository.getByUsername(diningReview.getUsername()) == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "User does not exist");
}
DiningReview newReview = diningReview;
newReview.setAdminReviewStatus(AdminReviewStatus.PENDING);
return diningReviewRepository.save(newReview);
}
// returns all reviews sorted by username
@GetMapping("/sorted_username")
public Iterable<DiningReview> getAllDiningReviewsSorted() {
return diningReviewRepository.findAll(Sort.by("username"));
}
// The following three endpoints return an Iterable of reviews by AdminReviewStatus Pending, Approved, Rejected
@GetMapping("/pending")
public Iterable<DiningReview> getPendingReviews() {
return this.diningReviewRepository.findByAdminReviewStatus(AdminReviewStatus.PENDING);
}
@GetMapping("/approved")
public Iterable<DiningReview> getApprovedReviews() {
return this.diningReviewRepository.findByAdminReviewStatus(AdminReviewStatus.APPROVED);
}
@GetMapping("/rejected")
public Iterable<DiningReview> getRejectedReviews() {
return this.diningReviewRepository.findByAdminReviewStatus(AdminReviewStatus.REJECTED);
}
// approves a pending review
@PutMapping("/admin_approve/{id}")
public DiningReview approveReview(@PathVariable("id") Long id) {
Optional<DiningReview> reviewToChangeOptional = diningReviewRepository.findById(id);
if (reviewToChangeOptional.isEmpty()) {
System.out.print("Review does not exist");
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Review id does not exist");
} else {
DiningReview reviewToChange = reviewToChangeOptional.get();
reviewToChange.setAdminReviewStatus(AdminReviewStatus.APPROVED);
diningReviewRepository.save(reviewToChange);
return reviewToChange;
}
}
// deletes a rejected review
@DeleteMapping("/delete_rejected/{id}")
public DiningReview deleteRejectedDiningReview(@PathVariable("id") Long id) {
Optional<DiningReview> reviewToDeleteOptional = diningReviewRepository.findById(id);
if (!reviewToDeleteOptional.isPresent()) {
return null;
}
DiningReview reviewToDelete = reviewToDeleteOptional.get();
diningReviewRepository.delete(reviewToDelete);
System.out.print("\nid: " + id + " has been successfully deleted");
return reviewToDelete;
}
Repositories for User, Restaurant, DiningReview
public interface UserRepository extends CrudRepository<User, Long> {
User getByUsername(String username);
Iterable <User> getByPeanutInterestTrue();
Iterable <User> getByEggInterestTrue();
Iterable <User> getByDairyInterestTrue();
}
public interface RestaurantRepository extends CrudRepository<Restaurant, Long> {
List<Restaurant> findAll(Sort name);
List<Restaurant> findByNameContaining(String name);
List<Restaurant> findByOverallRatingGreaterThanEqual(Double overallRating);
List<Restaurant> findByPeanutRatingGreaterThanEqual(Integer peanutRating);
List<Restaurant> findByEggRatingGreaterThanEqual(Integer eggRating);
List<Restaurant> findByDairyRatingGreaterThanEqual(Integer dairyRating);
}
public interface DiningReviewRepository extends CrudRepository<DiningReview, Long> {
Iterable<DiningReview> findByAdminReviewStatus(AdminReviewStatus adminReviewStatus);
Iterable<DiningReview> findByPeanutScoreGreaterThanEqual(Integer peanutScore);
Iterable<DiningReview> findByEggScoreGreaterThanEqual(Integer eggScore);
Iterable<DiningReview> findByDairyScoreGreaterThanEqual(Integer dairyScore);
}
Java | Spring | Hibernate | IntelliJ IDE |
Beth Meeker GH |