package app.mealsmadeeasy.api.recipe.comment; import app.mealsmadeeasy.api.markdown.MarkdownService; import app.mealsmadeeasy.api.recipe.RecipeEntity; import app.mealsmadeeasy.api.recipe.RecipeException; import app.mealsmadeeasy.api.recipe.RecipeRepository; import app.mealsmadeeasy.api.user.User; import app.mealsmadeeasy.api.user.UserEntity; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; import java.time.OffsetDateTime; import static java.util.Objects.requireNonNull; @Service public class RecipeCommentServiceImpl implements RecipeCommentService { private final RecipeCommentRepository recipeCommentRepository; private final RecipeRepository recipeRepository; private final MarkdownService markdownService; public RecipeCommentServiceImpl( RecipeCommentRepository recipeCommentRepository, RecipeRepository recipeRepository, MarkdownService markdownService ) { this.recipeCommentRepository = recipeCommentRepository; this.recipeRepository = recipeRepository; this.markdownService = markdownService; } @Override @PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #commenter)") public RecipeComment create( String recipeUsername, String recipeSlug, User commenter, RecipeCommentCreateBody body ) throws RecipeException { requireNonNull(commenter); final RecipeCommentEntity draft = new RecipeCommentEntity(); draft.setCreated(OffsetDateTime.now()); draft.setRawText(body.getText()); draft.setCachedRenderedText(this.markdownService.renderAndCleanMarkdown(body.getText())); draft.setOwner((UserEntity) commenter); final RecipeEntity recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug) .orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_USERNAME_OR_SLUG, "Invalid username or slug: " + recipeUsername + "/" + recipeSlug )); draft.setRecipe(recipe); return this.recipeCommentRepository.save(draft); } @PostAuthorize("@recipeSecurity.isViewableBy(returnObject.recipe, #viewer)") private RecipeCommentEntity loadCommentEntity(long commentId, User viewer) throws RecipeException { return this.recipeCommentRepository.findById(commentId).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_COMMENT_ID, "No such RecipeComment for id: " + commentId )); } @Override public RecipeComment get(long commentId, User viewer) throws RecipeException { return this.loadCommentEntity(commentId, viewer); } @Override @PreAuthorize("@recipeSecurity.isViewableBy(#recipeUsername, #recipeSlug, #viewer)") public Slice getComments(String recipeUsername, String recipeSlug, Pageable pageable, User viewer) throws RecipeException { final RecipeEntity recipe = this.recipeRepository.findByOwnerUsernameAndSlug(recipeUsername, recipeSlug).orElseThrow( () -> new RecipeException( RecipeException.Type.INVALID_USERNAME_OR_SLUG, "No such Recipe for username/slug: " + recipeUsername + "/" + recipeSlug ) ); final Slice commentEntities = this.recipeCommentRepository.findAllByRecipe(recipe, pageable); return commentEntities.map(commentEntity -> RecipeCommentView.from( commentEntity, false )); } @Override public RecipeComment update(long commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException { final RecipeCommentEntity entity = this.loadCommentEntity(commentId, viewer); entity.setRawText(spec.getRawText()); return this.recipeCommentRepository.save(entity); } @PostAuthorize("@recipeSecurity.isOwner(returnObject.recipe, #modifier)") private RecipeCommentEntity loadForDelete(long commentId, User modifier) throws RecipeException { return this.recipeCommentRepository.findById(commentId).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_COMMENT_ID, "No such RecipeComment for id: " + commentId )); } @Override public void delete(long commentId, User modifier) throws RecipeException { final RecipeCommentEntity entityToDelete = this.loadForDelete(commentId, modifier); this.recipeCommentRepository.delete(entityToDelete); } }