185 lines
8.2 KiB
Java
185 lines
8.2 KiB
Java
package app.mealsmadeeasy.api.recipe;
|
|
|
|
import app.mealsmadeeasy.api.recipe.body.RecipeAiSearchBody;
|
|
import app.mealsmadeeasy.api.recipe.body.RecipeSearchBody;
|
|
import app.mealsmadeeasy.api.recipe.body.RecipeUpdateBody;
|
|
import app.mealsmadeeasy.api.recipe.comment.RecipeComment;
|
|
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentCreateBody;
|
|
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentService;
|
|
import app.mealsmadeeasy.api.recipe.comment.RecipeCommentView;
|
|
import app.mealsmadeeasy.api.recipe.converter.RecipeToFullViewConverter;
|
|
import app.mealsmadeeasy.api.recipe.converter.RecipeToInfoViewConverter;
|
|
import app.mealsmadeeasy.api.recipe.spec.RecipeUpdateSpec;
|
|
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.RecipeInfoView;
|
|
import app.mealsmadeeasy.api.sliceview.SliceView;
|
|
import app.mealsmadeeasy.api.sliceview.SliceViewService;
|
|
import app.mealsmadeeasy.api.user.User;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import lombok.RequiredArgsConstructor;
|
|
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.core.annotation.AuthenticationPrincipal;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
@RestController
|
|
@RequestMapping("/recipes")
|
|
@RequiredArgsConstructor
|
|
public class RecipesController {
|
|
|
|
private final RecipeService recipeService;
|
|
private final RecipeStarService recipeStarService;
|
|
private final RecipeCommentService recipeCommentService;
|
|
private final SliceViewService sliceViewService;
|
|
private final ObjectMapper objectMapper;
|
|
private final RecipeToFullViewConverter recipeToFullViewConverter;
|
|
private final RecipeToInfoViewConverter recipeToInfoViewConverter;
|
|
|
|
private Map<String, Object> getFullViewWrapper(String username, String slug, FullRecipeView view, @Nullable User viewer) {
|
|
Map<String, Object> wrapper = new HashMap<>();
|
|
wrapper.put("recipe", view);
|
|
wrapper.put("isStarred", this.recipeService.isStarer(username, slug, viewer));
|
|
wrapper.put("isOwner", this.recipeService.isOwner(username, slug, viewer));
|
|
return wrapper;
|
|
}
|
|
|
|
@GetMapping("/{username}/{slug}")
|
|
public ResponseEntity<Map<String, Object>> getByUsernameAndSlug(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
@RequestParam(defaultValue = "false") boolean includeRawText,
|
|
@AuthenticationPrincipal User viewer
|
|
) {
|
|
final Recipe recipe = this.recipeService.getByUsernameAndSlug(username, slug, viewer);
|
|
final FullRecipeView view = this.recipeToFullViewConverter.convert(recipe, includeRawText, viewer);
|
|
return ResponseEntity.ok(this.getFullViewWrapper(username, slug, view, viewer));
|
|
}
|
|
|
|
@PostMapping("/{username}/{slug}")
|
|
public ResponseEntity<Map<String, Object>> updateByUsernameAndSlug(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
@RequestParam(defaultValue = "true") boolean includeRawText,
|
|
@RequestBody RecipeUpdateBody updateBody,
|
|
@AuthenticationPrincipal User principal
|
|
) {
|
|
final RecipeUpdateSpec spec = RecipeUpdateSpec.from(updateBody);
|
|
final Recipe updated = this.recipeService.update(username, slug, spec, principal);
|
|
final FullRecipeView view = this.recipeToFullViewConverter.convert(updated, includeRawText, principal);
|
|
return ResponseEntity.ok(this.getFullViewWrapper(username, slug, view, principal));
|
|
}
|
|
|
|
@GetMapping
|
|
public ResponseEntity<SliceView<RecipeInfoView>> getRecipeInfoViews(
|
|
Pageable pageable,
|
|
@AuthenticationPrincipal User user
|
|
) {
|
|
if (user == null) {
|
|
final Slice<Recipe> publicRecipes = this.recipeService.getPublicRecipes(pageable);
|
|
final Slice<RecipeInfoView> publicRecipeInfoViews = publicRecipes.map(
|
|
recipe -> this.recipeToInfoViewConverter.convert(recipe, null)
|
|
);
|
|
final int count = this.recipeService.countPublicRecipes();
|
|
return ResponseEntity.ok(this.sliceViewService.getSliceView(publicRecipeInfoViews, count));
|
|
} else {
|
|
final Slice<Recipe> recipes = this.recipeService.getViewableBy(pageable, user);
|
|
final Slice<RecipeInfoView> recipeInfoViews = recipes.map(
|
|
recipe -> this.recipeToInfoViewConverter.convert(recipe, user)
|
|
);
|
|
final int count = this.recipeService.countViewableBy(user);
|
|
return ResponseEntity.ok(this.sliceViewService.getSliceView(recipeInfoViews, count));
|
|
}
|
|
}
|
|
|
|
@PostMapping
|
|
public ResponseEntity<Map<String, Object>> searchRecipes(
|
|
@RequestBody(required = false) RecipeSearchBody recipeSearchBody,
|
|
@AuthenticationPrincipal User user
|
|
) {
|
|
if (recipeSearchBody.getType() == RecipeSearchBody.Type.AI_PROMPT) {
|
|
final RecipeAiSearchBody spec = this.objectMapper.convertValue(
|
|
recipeSearchBody.getData(),
|
|
RecipeAiSearchBody.class
|
|
);
|
|
final List<RecipeInfoView> results = this.recipeService.aiSearch(spec, user)
|
|
.stream()
|
|
.map(recipe -> this.recipeToInfoViewConverter.convert(recipe, user))
|
|
.toList();
|
|
return ResponseEntity.ok(Map.of("results", results));
|
|
} else {
|
|
throw new IllegalArgumentException("Invalid recipeSearchBody type: " + recipeSearchBody.getType());
|
|
}
|
|
}
|
|
|
|
@PostMapping("/{username}/{slug}/star")
|
|
public ResponseEntity<RecipeStar> addStar(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
@Nullable @AuthenticationPrincipal User principal
|
|
) {
|
|
return ResponseEntity.status(HttpStatus.CREATED).body(this.recipeStarService.create(username, slug, principal));
|
|
}
|
|
|
|
@GetMapping("/{username}/{slug}/star")
|
|
public ResponseEntity<Map<String, Object>> getStar(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
@Nullable @AuthenticationPrincipal User principal
|
|
) {
|
|
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<Object> removeStar(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
@Nullable @AuthenticationPrincipal User principal
|
|
) {
|
|
this.recipeStarService.delete(username, slug, principal);
|
|
return ResponseEntity.noContent().build();
|
|
}
|
|
|
|
@GetMapping("/{username}/{slug}/comments")
|
|
public ResponseEntity<SliceView<RecipeCommentView>> getComments(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
Pageable pageable,
|
|
@Nullable @AuthenticationPrincipal User principal
|
|
) {
|
|
final Slice<RecipeCommentView> slice = this.recipeCommentService.getComments(
|
|
username,
|
|
slug,
|
|
pageable,
|
|
principal
|
|
);
|
|
final int count = this.recipeCommentService.countComments(username, slug);
|
|
return ResponseEntity.ok(this.sliceViewService.getSliceView(slice, count));
|
|
}
|
|
|
|
@PostMapping("/{username}/{slug}/comments")
|
|
public ResponseEntity<RecipeCommentView> addComment(
|
|
@PathVariable String username,
|
|
@PathVariable String slug,
|
|
@RequestBody RecipeCommentCreateBody body,
|
|
@Nullable @AuthenticationPrincipal User principal
|
|
) {
|
|
final RecipeComment comment = this.recipeCommentService.create(username, slug, principal, body);
|
|
return ResponseEntity.ok(RecipeCommentView.from(comment, false));
|
|
}
|
|
|
|
}
|