package app.mealsmadeeasy.api.recipe; import app.mealsmadeeasy.api.recipe.star.RecipeStar; import app.mealsmadeeasy.api.recipe.star.RecipeStarService; import app.mealsmadeeasy.api.recipe.view.FullRecipeView; import app.mealsmadeeasy.api.recipe.view.RecipeExceptionView; import app.mealsmadeeasy.api.recipe.view.RecipeInfoView; import app.mealsmadeeasy.api.user.User; import org.jetbrains.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/recipes") public class RecipeController { private final RecipeService recipeService; private final RecipeStarService recipeStarService; public RecipeController(RecipeService recipeService, RecipeStarService recipeStarService) { this.recipeService = recipeService; this.recipeStarService = recipeStarService; } @ExceptionHandler(RecipeException.class) public ResponseEntity onRecipeException(RecipeException recipeException) { final HttpStatus status = switch (recipeException.getType()) { case INVALID_ID, INVALID_USERNAME_OR_SLUG -> HttpStatus.NOT_FOUND; case INVALID_COMMENT_ID -> HttpStatus.BAD_REQUEST; }; return ResponseEntity.status(status.value()).body(new RecipeExceptionView( recipeException.getType().toString(), recipeException.getMessage() )); } @GetMapping("/{username}/{slug}") public ResponseEntity> getByUsernameAndSlug( @PathVariable String username, @PathVariable String slug, @RequestParam(defaultValue = "false") boolean includeRawText, @AuthenticationPrincipal User viewer ) throws RecipeException { final FullRecipeView view = this.recipeService.getFullViewByUsernameAndSlug( username, slug, includeRawText, viewer ); final Map body = new HashMap<>(); body.put("recipe", view); body.put("isStarred", this.recipeService.isStarer(username, slug, viewer)); body.put("isOwner", this.recipeService.isOwner(username, slug, viewer)); return ResponseEntity.ok(body); } @GetMapping public ResponseEntity> getRecipeInfoViews( Pageable pageable, @AuthenticationPrincipal User user ) { final Slice slice = this.recipeService.getInfoViewsViewableBy(pageable, user); final Map view = new HashMap<>(); view.put("content", slice.getContent()); final Map sliceInfo = new HashMap<>(); sliceInfo.put("size", slice.getSize()); sliceInfo.put("number", slice.getNumber()); view.put("slice", sliceInfo); return ResponseEntity.ok(view); } @PostMapping("/{username}/{slug}/star") public ResponseEntity addStar( @PathVariable String username, @PathVariable String slug, @Nullable @AuthenticationPrincipal User principal ) throws RecipeException { if (principal == null) { throw new AccessDeniedException("Must be logged in to star a recipe."); } return ResponseEntity.status(HttpStatus.CREATED).body(this.recipeStarService.create(username, slug, principal)); } @GetMapping("/{username}/{slug}/star") public ResponseEntity> getStar( @PathVariable String username, @PathVariable String slug, @Nullable @AuthenticationPrincipal User principal ) throws RecipeException { if (principal == null) { throw new AccessDeniedException("Must be logged in to get a recipe star."); } final @Nullable RecipeStar star = this.recipeStarService.find(username, slug, principal).orElse(null); if (star != null) { return ResponseEntity.ok(Map.of("isStarred", true, "star", star)); } else { return ResponseEntity.ok(Map.of("isStarred", false)); } } @DeleteMapping("/{username}/{slug}/star") public ResponseEntity removeStar( @PathVariable String username, @PathVariable String slug, @Nullable @AuthenticationPrincipal User principal ) throws RecipeException { if (principal == null) { throw new AccessDeniedException("Must be logged in to delete a recipe star."); } this.recipeStarService.delete(username, slug, principal); return ResponseEntity.noContent().build(); } }