From 51cae79daa3e0c3b1be5939f7277f083c940ec3e Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Thu, 15 Jan 2026 22:18:08 -0600 Subject: [PATCH] Clean up recipe classes. --- .../api/recipe/RecipeControllerTests.java | 66 ++++--- .../api/recipe/RecipeServiceTests.java | 39 ++-- .../star/RecipeStarRepositoryTests.java | 2 +- .../recipe/star/RecipeStarServiceTests.java | 11 +- .../mealsmadeeasy/api/DevConfiguration.java | 13 +- .../app/mealsmadeeasy/api/recipe/Recipe.java | 1 + .../api/recipe/RecipeController.java | 12 +- .../api/recipe/RecipeRepository.java | 2 +- .../api/recipe/RecipeSecurity.java | 4 +- .../api/recipe/RecipeSecurityImpl.java | 4 +- .../api/recipe/RecipeService.java | 18 +- .../api/recipe/RecipeServiceImpl.java | 108 ++++++---- .../api/recipe/body/RecipeAiSearchBody.java | 8 + .../api/recipe/body/RecipeSearchBody.java | 19 +- .../api/recipe/body/RecipeUpdateBody.java | 23 +++ .../api/recipe/comment/RecipeComment.java | 1 + .../comment/RecipeCommentCreateBody.java | 13 +- .../comment/RecipeCommentRepository.java | 2 +- .../recipe/comment/RecipeCommentService.java | 6 +- .../comment/RecipeCommentServiceImpl.java | 10 +- .../comment/RecipeCommentUpdateSpec.java | 17 +- .../api/recipe/comment/RecipeCommentView.java | 92 ++------- .../api/recipe/spec/RecipeAiSearchSpec.java | 15 -- .../api/recipe/spec/RecipeCreateSpec.java | 86 ++------ .../api/recipe/spec/RecipeUpdateSpec.java | 151 +++++--------- .../api/recipe/star/RecipeStar.java | 2 +- .../api/recipe/star/RecipeStarId.java | 39 +--- .../recipe/star/RecipeStarServiceImpl.java | 2 +- .../api/recipe/view/FullRecipeView.java | 187 ++++-------------- .../api/recipe/view/RecipeExceptionView.java | 11 +- .../api/recipe/view/RecipeInfoView.java | 58 +++--- 31 files changed, 372 insertions(+), 650 deletions(-) create mode 100644 src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeAiSearchBody.java create mode 100644 src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeUpdateBody.java delete mode 100644 src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeAiSearchSpec.java diff --git a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeControllerTests.java b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeControllerTests.java index 958727d..fd80b2b 100644 --- a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeControllerTests.java +++ b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeControllerTests.java @@ -93,14 +93,15 @@ public class RecipeControllerTests { } private Recipe createTestRecipe(User owner, boolean isPublic) { - final RecipeCreateSpec spec = new RecipeCreateSpec(); - spec.setSlug(UUID.randomUUID().toString()); - spec.setTitle("Test Recipe"); - spec.setPreparationTime(10); - spec.setCookingTime(20); - spec.setTotalTime(30); - spec.setRawText("# Hello, World!"); - spec.setPublic(isPublic); + final RecipeCreateSpec spec = RecipeCreateSpec.builder() + .slug(UUID.randomUUID().toString()) + .title("Test Recipe") + .preparationTime(10) + .cookingTime(20) + .totalTime(30) + .rawText("# Hello, World!") + .isPublic(isPublic) + .build(); return this.recipeService.create(owner, spec); } @@ -146,7 +147,7 @@ public class RecipeControllerTests { .andExpect(jsonPath("$.recipe.owner.username").value(owner.getUsername())) .andExpect(jsonPath("$.recipe.starCount").value(0)) .andExpect(jsonPath("$.recipe.viewerCount").value(0)) - .andExpect(jsonPath("$.recipe.isPublic").value(true)) + .andExpect(jsonPath("$.recipe.public").value(true)) .andExpect(jsonPath("$.recipe.mainImage").value(nullValue())) .andExpect(jsonPath("$.isStarred").value(nullValue())) .andExpect(jsonPath("$.isOwner").value(nullValue())); @@ -225,13 +226,14 @@ public class RecipeControllerTests { } private String getUpdateBody() throws JsonProcessingException { - final RecipeUpdateSpec spec = new RecipeUpdateSpec(); - spec.setTitle("Updated Test Recipe"); - spec.setPreparationTime(15); - spec.setCookingTime(30); - spec.setTotalTime(45); - spec.setRawText("# Hello, Updated World!"); - spec.setIsPublic(true); + final RecipeUpdateSpec spec = RecipeUpdateSpec.builder() + .title("Updated Test Recipe") + .preparationTime(15) + .cookingTime(30) + .totalTime(45) + .rawText("# Hello, Updated World!") + .isPublic(true) + .build(); return this.objectMapper.writeValueAsString(spec); } @@ -259,7 +261,7 @@ public class RecipeControllerTests { .andExpect(jsonPath("$.recipe.owner.username").value(owner.getUsername())) .andExpect(jsonPath("$.recipe.starCount").value(0)) .andExpect(jsonPath("$.recipe.viewerCount").value(0)) - .andExpect(jsonPath("$.recipe.isPublic").value(true)) + .andExpect(jsonPath("$.recipe.public").value(true)) .andExpect(jsonPath("$.recipe.mainImage").value(nullValue())) .andExpect(jsonPath("$.isStarred").value(false)) .andExpect(jsonPath("$.isOwner").value(true)); @@ -271,21 +273,25 @@ public class RecipeControllerTests { final Image hal9000 = this.createHal9000(owner); - final RecipeCreateSpec createSpec = new RecipeCreateSpec(); - createSpec.setTitle("Test Recipe"); - createSpec.setSlug("test-recipe"); - createSpec.setPublic(false); - createSpec.setRawText("# Hello, World!"); - createSpec.setMainImage(hal9000); + final RecipeCreateSpec createSpec = RecipeCreateSpec.builder() + .title("Test Recipe") + .slug(UUID.randomUUID().toString()) + .isPublic(false) + .rawText("# Hello, World!") + .mainImage(hal9000) + .build(); Recipe recipe = this.recipeService.create(owner, createSpec); - final RecipeUpdateSpec updateSpec = new RecipeUpdateSpec(); - updateSpec.setTitle("Updated Test Recipe"); - updateSpec.setRawText("# Hello, Updated World!"); - final RecipeUpdateSpec.MainImageUpdateSpec mainImageUpdateSpec = new RecipeUpdateSpec.MainImageUpdateSpec(); - mainImageUpdateSpec.setUsername(hal9000.getOwner().getUsername()); - mainImageUpdateSpec.setFilename(hal9000.getUserFilename()); - updateSpec.setMainImage(mainImageUpdateSpec); + final RecipeUpdateSpec updateSpec = RecipeUpdateSpec.builder() + .title("Updated Test Recipe") + .rawText("# Hello, Updated World!") + .mainImage( + RecipeUpdateSpec.MainImageUpdateSpec.builder() + .username(hal9000.getOwner().getUsername()) + .filename(hal9000.getUserFilename()) + .build() + ) + .build(); final String body = this.objectMapper.writeValueAsString(updateSpec); final String accessToken = this.getAccessToken(owner); diff --git a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeServiceTests.java b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeServiceTests.java index c0a4e12..7c5cbed 100644 --- a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeServiceTests.java +++ b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/RecipeServiceTests.java @@ -58,11 +58,12 @@ public class RecipeServiceTests { } private Recipe createTestRecipe(@Nullable User owner, boolean isPublic) { - final RecipeCreateSpec spec = new RecipeCreateSpec(); - spec.setSlug(UUID.randomUUID().toString()); - spec.setTitle("My Recipe"); - spec.setRawText("Hello!"); - spec.setPublic(isPublic); + final RecipeCreateSpec spec = RecipeCreateSpec.builder() + .slug(UUID.randomUUID().toString()) + .title("My Recipe") + .rawText("Hello!") + .isPublic(isPublic) + .build(); return this.recipeService.create(owner, spec); } @@ -80,7 +81,9 @@ public class RecipeServiceTests { @Test public void createWithoutOwnerThrowsAccessDenied() { - assertThrows(AccessDeniedException.class, () -> this.recipeService.create(null, new RecipeCreateSpec())); + assertThrows(AccessDeniedException.class, () -> this.recipeService.create( + null, RecipeCreateSpec.builder().build() + )); } @Test @@ -156,8 +159,9 @@ public class RecipeServiceTests { final User owner = this.seedUser(); final User viewer = this.seedUser(); final Recipe notYetPublicRecipe = this.createTestRecipe(owner); - final RecipeUpdateSpec updateSpec = new RecipeUpdateSpec(notYetPublicRecipe); - updateSpec.setIsPublic(true); + final RecipeUpdateSpec updateSpec = RecipeUpdateSpec.fromRecipeToBuilder(notYetPublicRecipe) + .isPublic(true) + .build(); final Recipe publicRecipe = this.recipeService.update( notYetPublicRecipe.getOwner().getUsername(), notYetPublicRecipe.getSlug(), @@ -284,14 +288,16 @@ public class RecipeServiceTests { @Test public void updateRawText() throws RecipeException, ImageException { final User owner = this.seedUser(); - final RecipeCreateSpec createSpec = new RecipeCreateSpec(); - createSpec.setSlug("my-recipe"); - createSpec.setTitle("My Recipe"); - createSpec.setRawText("# A Heading"); + final RecipeCreateSpec createSpec = RecipeCreateSpec.builder() + .slug(UUID.randomUUID().toString()) + .title("My Recipe") + .rawText("# A Heading") + .build(); Recipe recipe = this.recipeService.create(owner, createSpec); final String newRawText = "# A Heading\n## A Subheading"; - final RecipeUpdateSpec updateSpec = new RecipeUpdateSpec(recipe); - updateSpec.setRawText(newRawText); + final RecipeUpdateSpec updateSpec = RecipeUpdateSpec.fromRecipeToBuilder(recipe) + .rawText(newRawText) + .build(); recipe = this.recipeService.update( recipe.getOwner().getUsername(), recipe.getSlug(), @@ -306,8 +312,9 @@ public class RecipeServiceTests { final User owner = this.seedUser(); final User notOwner = this.seedUser(); final Recipe recipe = this.createTestRecipe(owner); - final RecipeUpdateSpec updateSpec = new RecipeUpdateSpec(); - updateSpec.setRawText("should fail"); + final RecipeUpdateSpec updateSpec = RecipeUpdateSpec.fromRecipeToBuilder(recipe) + .rawText("should fail") + .build(); assertThrows( AccessDeniedException.class, () -> this.recipeService.update( diff --git a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarRepositoryTests.java b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarRepositoryTests.java index 78ce2b6..e6ff4bc 100644 --- a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarRepositoryTests.java +++ b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarRepositoryTests.java @@ -56,7 +56,7 @@ public class RecipeStarRepositoryTests { final RecipeStar starDraft = new RecipeStar(); final RecipeStarId starId = new RecipeStarId(); starId.setRecipeId(recipe.getId()); - starId.getOwnerId(owner.getId()); + starId.setOwnerId(owner.getId()); starDraft.setId(starId); this.recipeStarRepository.save(starDraft); diff --git a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceTests.java b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceTests.java index e6f67f5..1b69bfb 100644 --- a/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceTests.java +++ b/src/integrationTest/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceTests.java @@ -44,11 +44,12 @@ public class RecipeStarServiceTests { } private Recipe seedRecipe(User owner) { - final RecipeCreateSpec spec = new RecipeCreateSpec(); - spec.setSlug(UUID.randomUUID().toString()); - spec.setTitle("Test Recipe"); - spec.setRawText("My great recipe has five ingredients."); - spec.setPublic(true); + final RecipeCreateSpec spec = RecipeCreateSpec.builder() + .slug(UUID.randomUUID().toString()) + .title("Test Recipe") + .rawText("My great recipe has five ingredients.") + .isPublic(true) + .build(); return this.recipeService.create(owner, spec); } diff --git a/src/main/java/app/mealsmadeeasy/api/DevConfiguration.java b/src/main/java/app/mealsmadeeasy/api/DevConfiguration.java index 5216d6a..6663b85 100644 --- a/src/main/java/app/mealsmadeeasy/api/DevConfiguration.java +++ b/src/main/java/app/mealsmadeeasy/api/DevConfiguration.java @@ -102,12 +102,13 @@ public class DevConfiguration { logger.info("Created mainImage {} for {}", mainImage, recipePath); } - final RecipeCreateSpec recipeCreateSpec = new RecipeCreateSpec(); - recipeCreateSpec.setSlug(frontMatter.slug); - recipeCreateSpec.setTitle(frontMatter.title); - recipeCreateSpec.setRawText(rawRecipeText); - recipeCreateSpec.setPublic(frontMatter.isPublic); - recipeCreateSpec.setMainImage(mainImage); + final RecipeCreateSpec recipeCreateSpec = RecipeCreateSpec.builder() + .slug(frontMatter.slug) + .title(frontMatter.title) + .rawText(rawRecipeText) + .isPublic(frontMatter.isPublic) + .mainImage(mainImage) + .build(); final Recipe recipe = this.recipeService.create(testUser, recipeCreateSpec); logger.info("Created recipe {}", recipe); } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/Recipe.java b/src/main/java/app/mealsmadeeasy/api/recipe/Recipe.java index ca507f2..7162179 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/Recipe.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/Recipe.java @@ -75,6 +75,7 @@ public final class Recipe { @ManyToOne @JoinColumn(name = "main_image_id") + @Nullable private Image mainImage; @OneToOne(mappedBy = "recipe", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeController.java b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeController.java index 1687aad..72839ae 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeController.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeController.java @@ -1,12 +1,13 @@ package app.mealsmadeeasy.api.recipe; import app.mealsmadeeasy.api.image.ImageException; +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.spec.RecipeAiSearchSpec; import app.mealsmadeeasy.api.recipe.spec.RecipeUpdateSpec; import app.mealsmadeeasy.api.recipe.star.RecipeStar; import app.mealsmadeeasy.api.recipe.star.RecipeStarService; @@ -94,10 +95,11 @@ public class RecipeController { @PathVariable String username, @PathVariable String slug, @RequestParam(defaultValue = "true") boolean includeRawText, - @RequestBody RecipeUpdateSpec updateSpec, + @RequestBody RecipeUpdateBody updateBody, @AuthenticationPrincipal User principal ) throws ImageException, RecipeException { - final Recipe updated = this.recipeService.update(username, slug, updateSpec, principal); + final RecipeUpdateSpec spec = RecipeUpdateSpec.from(updateBody); + final Recipe updated = this.recipeService.update(username, slug, spec, principal); final FullRecipeView view = this.recipeService.toFullRecipeView(updated, includeRawText, principal); return ResponseEntity.ok(this.getFullViewWrapper(username, slug, view, principal)); } @@ -117,9 +119,9 @@ public class RecipeController { @AuthenticationPrincipal User user ) { if (recipeSearchBody.getType() == RecipeSearchBody.Type.AI_PROMPT) { - final RecipeAiSearchSpec spec = this.objectMapper.convertValue( + final RecipeAiSearchBody spec = this.objectMapper.convertValue( recipeSearchBody.getData(), - RecipeAiSearchSpec.class + RecipeAiSearchBody.class ); final List results = this.recipeService.aiSearch(spec, user); return ResponseEntity.ok(Map.of("results", results)); diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeRepository.java b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeRepository.java index 4aeaf6f..0d68a44 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeRepository.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeRepository.java @@ -10,7 +10,7 @@ import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; -public interface RecipeRepository extends JpaRepository { +public interface RecipeRepository extends JpaRepository { List findAllByIsPublicIsTrue(); diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurity.java b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurity.java index 99f43a6..de3b273 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurity.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurity.java @@ -5,9 +5,9 @@ import org.jetbrains.annotations.Nullable; public interface RecipeSecurity { boolean isOwner(Recipe recipe, User user); - boolean isOwner(long recipeId, User user) throws RecipeException; + boolean isOwner(Integer recipeId, User user) throws RecipeException; boolean isOwner(String username, String slug, @Nullable User user) throws RecipeException; boolean isViewableBy(Recipe recipe, @Nullable User user) throws RecipeException; boolean isViewableBy(String ownerUsername, String slug, @Nullable User user) throws RecipeException; - boolean isViewableBy(long recipeId, @Nullable User user) throws RecipeException; + boolean isViewableBy(Integer recipeId, @Nullable User user) throws RecipeException; } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurityImpl.java b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurityImpl.java index 540a33a..d3ddffc 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurityImpl.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeSecurityImpl.java @@ -21,7 +21,7 @@ public class RecipeSecurityImpl implements RecipeSecurity { } @Override - public boolean isOwner(long recipeId, User user) throws RecipeException { + public boolean isOwner(Integer recipeId, User user) throws RecipeException { final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_ID, "No such Recipe with id " + recipeId @@ -78,7 +78,7 @@ public class RecipeSecurityImpl implements RecipeSecurity { } @Override - public boolean isViewableBy(long recipeId, @Nullable User user) throws RecipeException { + public boolean isViewableBy(Integer recipeId, @Nullable User user) throws RecipeException { final Recipe recipe = this.recipeRepository.findById(recipeId).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_ID, "No such Recipe with id: " + recipeId diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeService.java b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeService.java index 466d6f3..fd4cfce 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeService.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeService.java @@ -1,7 +1,7 @@ package app.mealsmadeeasy.api.recipe; import app.mealsmadeeasy.api.image.ImageException; -import app.mealsmadeeasy.api.recipe.spec.RecipeAiSearchSpec; +import app.mealsmadeeasy.api.recipe.body.RecipeAiSearchBody; import app.mealsmadeeasy.api.recipe.spec.RecipeCreateSpec; import app.mealsmadeeasy.api.recipe.spec.RecipeUpdateSpec; import app.mealsmadeeasy.api.recipe.view.FullRecipeView; @@ -19,11 +19,11 @@ public interface RecipeService { Recipe create(@Nullable User owner, RecipeCreateSpec spec); - Recipe getById(long id, @Nullable User viewer) throws RecipeException; - Recipe getByIdWithStars(long id, @Nullable User viewer) throws RecipeException; + Recipe getById(Integer id, @Nullable User viewer) throws RecipeException; + Recipe getByIdWithStars(Integer id, @Nullable User viewer) throws RecipeException; Recipe getByUsernameAndSlug(String username, String slug, @Nullable User viewer) throws RecipeException; - FullRecipeView getFullViewById(long id, @Nullable User viewer) throws RecipeException; + FullRecipeView getFullViewById(Integer id, @Nullable User viewer) throws RecipeException; FullRecipeView getFullViewByUsernameAndSlug( String username, String slug, @@ -37,16 +37,16 @@ public interface RecipeService { List getRecipesViewableBy(User viewer); List getRecipesOwnedBy(User owner); - List aiSearch(RecipeAiSearchSpec searchSpec, @Nullable User viewer); + List aiSearch(RecipeAiSearchBody searchSpec, @Nullable User viewer); Recipe update(String username, String slug, RecipeUpdateSpec spec, User modifier) throws RecipeException, ImageException; - Recipe addViewer(long id, User modifier, User viewer) throws RecipeException; - Recipe removeViewer(long id, User modifier, User viewer) throws RecipeException; - Recipe clearAllViewers(long id, User modifier) throws RecipeException; + Recipe addViewer(Integer id, User modifier, User viewer) throws RecipeException; + Recipe removeViewer(Integer id, User modifier, User viewer) throws RecipeException; + Recipe clearAllViewers(Integer id, User modifier) throws RecipeException; - void deleteRecipe(long id, User modifier); + void deleteRecipe(Integer id, User modifier); FullRecipeView toFullRecipeView(Recipe recipe, boolean includeRawText, @Nullable User viewer); RecipeInfoView toRecipeInfoView(Recipe recipe, @Nullable User viewer); diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeServiceImpl.java b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeServiceImpl.java index 6b3a161..bc355c0 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/RecipeServiceImpl.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/RecipeServiceImpl.java @@ -5,7 +5,7 @@ import app.mealsmadeeasy.api.image.ImageException; import app.mealsmadeeasy.api.image.ImageService; import app.mealsmadeeasy.api.image.view.ImageView; import app.mealsmadeeasy.api.markdown.MarkdownService; -import app.mealsmadeeasy.api.recipe.spec.RecipeAiSearchSpec; +import app.mealsmadeeasy.api.recipe.body.RecipeAiSearchBody; import app.mealsmadeeasy.api.recipe.spec.RecipeCreateSpec; import app.mealsmadeeasy.api.recipe.spec.RecipeUpdateSpec; import app.mealsmadeeasy.api.recipe.star.RecipeStarRepository; @@ -58,16 +58,16 @@ public class RecipeServiceImpl implements RecipeService { } final Recipe draft = new Recipe(); draft.setCreated(OffsetDateTime.now()); - draft.setOwner((User) owner); + draft.setOwner(owner); draft.setSlug(spec.getSlug()); draft.setTitle(spec.getTitle()); draft.setRawText(spec.getRawText()); - draft.setMainImage((Image) spec.getMainImage()); + draft.setMainImage(spec.getMainImage()); draft.setIsPublic(spec.isPublic()); return this.recipeRepository.save(draft); } - private Recipe findRecipeEntity(long id) throws RecipeException { + private Recipe findRecipeEntity(Integer id) throws RecipeException { return this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id )); @@ -75,13 +75,13 @@ public class RecipeServiceImpl implements RecipeService { @Override @PostAuthorize("@recipeSecurity.isViewableBy(returnObject, #viewer)") - public Recipe getById(long id, User viewer) throws RecipeException { + public Recipe getById(Integer id, User viewer) throws RecipeException { return this.findRecipeEntity(id); } @Override @PostAuthorize("@recipeSecurity.isViewableBy(returnObject, #viewer)") - public Recipe getByIdWithStars(long id, @Nullable User viewer) throws RecipeException { + public Recipe getByIdWithStars(Integer id, @Nullable User viewer) throws RecipeException { return this.recipeRepository.findByIdWithStars(id).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id @@ -145,7 +145,7 @@ public class RecipeServiceImpl implements RecipeService { @Override @PreAuthorize("@recipeSecurity.isViewableBy(#id, #viewer)") - public FullRecipeView getFullViewById(long id, @Nullable User viewer) throws RecipeException { + public FullRecipeView getFullViewById(Integer id, @Nullable User viewer) throws RecipeException { final Recipe recipe = this.recipeRepository.findById(id).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_ID, "No such Recipe for id: " + id )); @@ -170,7 +170,7 @@ public class RecipeServiceImpl implements RecipeService { @Override public Slice getInfoViewsViewableBy(Pageable pageable, @Nullable User viewer) { - return this.recipeRepository.findAllViewableBy((User) viewer, pageable).map(recipe -> + return this.recipeRepository.findAllViewableBy(viewer, pageable).map(recipe -> this.getInfoView(recipe, viewer) ); } @@ -178,7 +178,7 @@ public class RecipeServiceImpl implements RecipeService { @Override public List getByMinimumStars(long minimumStars, User viewer) { return List.copyOf( - this.recipeRepository.findAllViewableByStarsGreaterThanEqual(minimumStars, (User) viewer) + this.recipeRepository.findAllViewableByStarsGreaterThanEqual(minimumStars, viewer) ); } @@ -189,16 +189,16 @@ public class RecipeServiceImpl implements RecipeService { @Override public List getRecipesViewableBy(User viewer) { - return List.copyOf(this.recipeRepository.findAllByViewersContaining((User) viewer)); + return List.copyOf(this.recipeRepository.findAllByViewersContaining(viewer)); } @Override public List getRecipesOwnedBy(User owner) { - return List.copyOf(this.recipeRepository.findAllByOwner((User) owner)); + return List.copyOf(this.recipeRepository.findAllByOwner(owner)); } @Override - public List aiSearch(RecipeAiSearchSpec searchSpec, @Nullable User viewer) { + public List aiSearch(RecipeAiSearchBody searchSpec, @Nullable User viewer) { final float[] queryEmbedding = this.embeddingModel.embed(searchSpec.getPrompt()); final List results; if (viewer == null) { @@ -211,6 +211,51 @@ public class RecipeServiceImpl implements RecipeService { .toList(); } + private void prepareForUpdate(RecipeUpdateSpec spec, Recipe recipe, User modifier) throws ImageException { + boolean didUpdate = false; + if (spec.getTitle() != null) { + recipe.setTitle(spec.getTitle()); + didUpdate = true; + } + if (spec.getPreparationTime() != null) { + recipe.setPreparationTime(spec.getPreparationTime()); + didUpdate = true; + } + if (spec.getCookingTime() != null) { + recipe.setCookingTime(spec.getCookingTime()); + didUpdate = true; + } + if (spec.getTotalTime() != null) { + recipe.setTotalTime(spec.getTotalTime()); + didUpdate = true; + } + + if (spec.getRawText() != null) { + recipe.setRawText(spec.getRawText()); + recipe.setCachedRenderedText(null); + didUpdate = true; + } + + if (spec.getIsPublic() != null) { + recipe.setIsPublic(spec.getIsPublic()); + didUpdate = true; + } + + // TODO: we have to think about how to unset the main image vs. just leaving it out of the request + if (spec.getMainImage() != null) { + final Image mainImage = this.imageService.getByUsernameAndFilename( + spec.getMainImage().getUsername(), + spec.getMainImage().getFilename(), + modifier + ); + recipe.setMainImage(mainImage); + } + + if (didUpdate) { + recipe.setModified(OffsetDateTime.now()); + } + } + @Override @PreAuthorize("@recipeSecurity.isOwner(#username, #slug, #modifier)") public Recipe update(String username, String slug, RecipeUpdateSpec spec, User modifier) @@ -221,56 +266,35 @@ public class RecipeServiceImpl implements RecipeService { "No such Recipe for username " + username + " and slug: " + slug ) ); - - recipe.setTitle(spec.getTitle()); - recipe.setPreparationTime(spec.getPreparationTime()); - recipe.setCookingTime(spec.getCookingTime()); - recipe.setTotalTime(spec.getTotalTime()); - recipe.setRawText(spec.getRawText()); - recipe.setCachedRenderedText(null); - recipe.setIsPublic(spec.getIsPublic()); - - final Image mainImage; - if (spec.getMainImage() == null) { - mainImage = null; - } else { - mainImage = (Image) this.imageService.getByUsernameAndFilename( - spec.getMainImage().getUsername(), - spec.getMainImage().getFilename(), - modifier - ); - } - recipe.setMainImage(mainImage); - - recipe.setModified(OffsetDateTime.now()); + this.prepareForUpdate(spec, recipe, modifier); return this.recipeRepository.save(recipe); } @Override @PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)") - public Recipe addViewer(long id, User modifier, User viewer) throws RecipeException { + public Recipe addViewer(Integer id, User modifier, User viewer) throws RecipeException { final Recipe entity = this.recipeRepository.findByIdWithViewers(id).orElseThrow(() -> new RecipeException( RecipeException.Type.INVALID_ID, "No such Recipe with id: " + id )); final Set viewers = new HashSet<>(entity.getViewers()); - viewers.add((User) viewer); + viewers.add(viewer); entity.setViewers(viewers); return this.recipeRepository.save(entity); } @Override @PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)") - public Recipe removeViewer(long id, User modifier, User viewer) throws RecipeException { + public Recipe removeViewer(Integer id, User modifier, User viewer) throws RecipeException { final Recipe entity = this.findRecipeEntity(id); final Set viewers = new HashSet<>(entity.getViewers()); - viewers.remove((User) viewer); + viewers.remove(viewer); entity.setViewers(viewers); return this.recipeRepository.save(entity); } @Override @PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)") - public Recipe clearAllViewers(long id, User modifier) throws RecipeException { + public Recipe clearAllViewers(Integer id, User modifier) throws RecipeException { final Recipe entity = this.findRecipeEntity(id); entity.setViewers(new HashSet<>()); return this.recipeRepository.save(entity); @@ -278,18 +302,18 @@ public class RecipeServiceImpl implements RecipeService { @Override @PreAuthorize("@recipeSecurity.isOwner(#id, #modifier)") - public void deleteRecipe(long id, User modifier) { + public void deleteRecipe(Integer id, User modifier) { this.recipeRepository.deleteById(id); } @Override public FullRecipeView toFullRecipeView(Recipe recipe, boolean includeRawText, @Nullable User viewer) { - return this.getFullView((Recipe) recipe, includeRawText, viewer); + return this.getFullView(recipe, includeRawText, viewer); } @Override public RecipeInfoView toRecipeInfoView(Recipe recipe, @Nullable User viewer) { - return this.getInfoView((Recipe) recipe, viewer); + return this.getInfoView(recipe, viewer); } @Override diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeAiSearchBody.java b/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeAiSearchBody.java new file mode 100644 index 0000000..8ff0b7c --- /dev/null +++ b/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeAiSearchBody.java @@ -0,0 +1,8 @@ +package app.mealsmadeeasy.api.recipe.body; + +import lombok.Data; + +@Data +public class RecipeAiSearchBody { + private String prompt; +} diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeSearchBody.java b/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeSearchBody.java index 5660ac9..99cbb24 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeSearchBody.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeSearchBody.java @@ -1,7 +1,10 @@ package app.mealsmadeeasy.api.recipe.body; +import lombok.Data; + import java.util.Map; +@Data public class RecipeSearchBody { public enum Type { @@ -11,20 +14,4 @@ public class RecipeSearchBody { private Type type; private Map data; - public Type getType() { - return this.type; - } - - public void setType(Type type) { - this.type = type; - } - - public Map getData() { - return this.data; - } - - public void setData(Map data) { - this.data = data; - } - } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeUpdateBody.java b/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeUpdateBody.java new file mode 100644 index 0000000..158511a --- /dev/null +++ b/src/main/java/app/mealsmadeeasy/api/recipe/body/RecipeUpdateBody.java @@ -0,0 +1,23 @@ +package app.mealsmadeeasy.api.recipe.body; + +import lombok.Data; +import org.jetbrains.annotations.Nullable; + +@Data +public class RecipeUpdateBody { + + @Data + public static class MainImageUpdateBody { + private String username; + private String filename; + } + + private @Nullable String title; + private @Nullable Integer preparationTime; + private @Nullable Integer cookingTime; + private @Nullable Integer totalTime; + private @Nullable String rawText; + private @Nullable Boolean isPublic; + private @Nullable MainImageUpdateBody mainImage; + +} diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeComment.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeComment.java index a45babc..d3a7747 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeComment.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeComment.java @@ -8,6 +8,7 @@ import lombok.Data; import java.time.OffsetDateTime; @Entity +@Table(name = "recipe_comment") @Data public final class RecipeComment { diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentCreateBody.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentCreateBody.java index cdbb8ea..c057151 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentCreateBody.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentCreateBody.java @@ -1,15 +1,8 @@ package app.mealsmadeeasy.api.recipe.comment; +import lombok.Data; + +@Data public class RecipeCommentCreateBody { - private String text; - - public String getText() { - return this.text; - } - - public void setText(String text) { - this.text = text; - } - } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentRepository.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentRepository.java index edf7ae4..94fc54e 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentRepository.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentRepository.java @@ -5,7 +5,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; -public interface RecipeCommentRepository extends JpaRepository { +public interface RecipeCommentRepository extends JpaRepository { void deleteAllByRecipe(Recipe recipe); Slice findAllByRecipe(Recipe recipe, Pageable pageable); } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentService.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentService.java index 24ce1d3..def8fd4 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentService.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentService.java @@ -8,9 +8,9 @@ import org.springframework.data.domain.Slice; public interface RecipeCommentService { RecipeComment create(String recipeUsername, String recipeSlug, User owner, RecipeCommentCreateBody body) throws RecipeException; - RecipeComment get(long commentId, User viewer) throws RecipeException; + RecipeComment get(Integer commentId, User viewer) throws RecipeException; Slice getComments(String recipeUsername, String recipeSlug, Pageable pageable, User viewer) throws RecipeException; - RecipeComment update(long commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException; - void delete(long commentId, User modifier) throws RecipeException; + RecipeComment update(Integer commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException; + void delete(Integer commentId, User modifier) throws RecipeException; } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentServiceImpl.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentServiceImpl.java index 9c3fb2c..1c617af 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentServiceImpl.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentServiceImpl.java @@ -56,14 +56,14 @@ public class RecipeCommentServiceImpl implements RecipeCommentService { } @PostAuthorize("@recipeSecurity.isViewableBy(returnObject.recipe, #viewer)") - private RecipeComment loadCommentEntity(long commentId, User viewer) throws RecipeException { + private RecipeComment loadCommentEntity(Integer 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 { + public RecipeComment get(Integer commentId, User viewer) throws RecipeException { return this.loadCommentEntity(commentId, viewer); } @@ -84,21 +84,21 @@ public class RecipeCommentServiceImpl implements RecipeCommentService { } @Override - public RecipeComment update(long commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException { + public RecipeComment update(Integer commentId, User viewer, RecipeCommentUpdateSpec spec) throws RecipeException { final RecipeComment entity = this.loadCommentEntity(commentId, viewer); entity.setRawText(spec.getRawText()); return this.recipeCommentRepository.save(entity); } @PostAuthorize("@recipeSecurity.isOwner(returnObject.recipe, #modifier)") - private RecipeComment loadForDelete(long commentId, User modifier) throws RecipeException { + private RecipeComment loadForDelete(Integer 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 { + public void delete(Integer commentId, User modifier) throws RecipeException { final RecipeComment entityToDelete = this.loadForDelete(commentId, modifier); this.recipeCommentRepository.delete(entityToDelete); } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentUpdateSpec.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentUpdateSpec.java index 028c219..b2c22aa 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentUpdateSpec.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentUpdateSpec.java @@ -1,15 +1,10 @@ package app.mealsmadeeasy.api.recipe.comment; +import lombok.Builder; +import lombok.Value; + +@Value +@Builder public class RecipeCommentUpdateSpec { - - private String rawText; - - public String getRawText() { - return this.rawText; - } - - public void setRawText(String rawText) { - this.rawText = rawText; - } - + String rawText; } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentView.java b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentView.java index 1559193..e91130f 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentView.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/comment/RecipeCommentView.java @@ -1,88 +1,36 @@ package app.mealsmadeeasy.api.recipe.comment; import app.mealsmadeeasy.api.user.view.UserInfoView; +import lombok.Builder; +import lombok.Value; import org.jetbrains.annotations.Nullable; import java.time.OffsetDateTime; +@Value +@Builder public class RecipeCommentView { public static RecipeCommentView from(RecipeComment comment, boolean includeRawText) { - final RecipeCommentView view = new RecipeCommentView(); - view.setId(comment.getId()); - view.setCreated(comment.getCreated()); - view.setModified(comment.getModified()); - view.setText(((RecipeComment) comment).getCachedRenderedText()); + final var builder = RecipeCommentView.builder() + .id(comment.getId()) + .created(comment.getCreated()) + .modified(comment.getModified()) + .text(comment.getCachedRenderedText()) + .owner(UserInfoView.from(comment.getOwner())) + .recipeId(comment.getRecipe().getId()); if (includeRawText) { - view.setRawText(comment.getRawText()); + builder.rawText(comment.getRawText()); } - view.setOwner(UserInfoView.from(comment.getOwner())); - view.setRecipeId(comment.getRecipe().getId()); - return view; + return builder.build(); } - private Integer id; - private OffsetDateTime created; - private @Nullable OffsetDateTime modified; - private String text; - private @Nullable String rawText; - private UserInfoView owner; - private Integer recipeId; - - public Integer getId() { - return this.id; - } - - public void setId(Integer id) { - this.id = id; - } - - public OffsetDateTime getCreated() { - return this.created; - } - - public void setCreated(OffsetDateTime created) { - this.created = created; - } - - public @Nullable OffsetDateTime getModified() { - return this.modified; - } - - public void setModified(@Nullable OffsetDateTime modified) { - this.modified = modified; - } - - public String getText() { - return this.text; - } - - public void setText(String text) { - this.text = text; - } - - public @Nullable String getRawText() { - return this.rawText; - } - - public void setRawText(@Nullable String rawText) { - this.rawText = rawText; - } - - public UserInfoView getOwner() { - return this.owner; - } - - public void setOwner(UserInfoView owner) { - this.owner = owner; - } - - public Integer getRecipeId() { - return this.recipeId; - } - - public void setRecipeId(Integer recipeId) { - this.recipeId = recipeId; - } + Integer id; + OffsetDateTime created; + @Nullable OffsetDateTime modified; + String text; + @Nullable String rawText; + UserInfoView owner; + Integer recipeId; } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeAiSearchSpec.java b/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeAiSearchSpec.java deleted file mode 100644 index 37b0eb6..0000000 --- a/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeAiSearchSpec.java +++ /dev/null @@ -1,15 +0,0 @@ -package app.mealsmadeeasy.api.recipe.spec; - -public class RecipeAiSearchSpec { - - private String prompt; - - public String getPrompt() { - return this.prompt; - } - - public void setPrompt(String prompt) { - this.prompt = prompt; - } - -} diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeCreateSpec.java b/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeCreateSpec.java index 2bfb136..a46c1c4 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeCreateSpec.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeCreateSpec.java @@ -1,81 +1,19 @@ package app.mealsmadeeasy.api.recipe.spec; import app.mealsmadeeasy.api.image.Image; +import lombok.Builder; +import lombok.Value; import org.jetbrains.annotations.Nullable; +@Value +@Builder public class RecipeCreateSpec { - - private String slug; - private String title; - private @Nullable Integer preparationTime; - private @Nullable Integer cookingTime; - private @Nullable Integer totalTime; - private String rawText; - private boolean isPublic; - private @Nullable Image mainImage; - - public String getSlug() { - return this.slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public @Nullable Integer getPreparationTime() { - return this.preparationTime; - } - - public void setPreparationTime(@Nullable Integer preparationTime) { - this.preparationTime = preparationTime; - } - - public @Nullable Integer getCookingTime() { - return this.cookingTime; - } - - public void setCookingTime(@Nullable Integer cookingTime) { - this.cookingTime = cookingTime; - } - - public @Nullable Integer getTotalTime() { - return this.totalTime; - } - - public void setTotalTime(@Nullable Integer totalTime) { - this.totalTime = totalTime; - } - - public String getRawText() { - return this.rawText; - } - - public void setRawText(String rawText) { - this.rawText = rawText; - } - - public boolean isPublic() { - return this.isPublic; - } - - public void setPublic(boolean isPublic) { - this.isPublic = isPublic; - } - - public @Nullable Image getMainImage() { - return this.mainImage; - } - - public void setMainImage(@Nullable Image mainImage) { - this.mainImage = mainImage; - } - + String slug; + String title; + @Nullable Integer preparationTime; + @Nullable Integer cookingTime; + @Nullable Integer totalTime; + String rawText; + boolean isPublic; + @Nullable Image mainImage; } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeUpdateSpec.java b/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeUpdateSpec.java index d511f1f..9e8ec5a 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeUpdateSpec.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/spec/RecipeUpdateSpec.java @@ -2,120 +2,73 @@ package app.mealsmadeeasy.api.recipe.spec; import app.mealsmadeeasy.api.image.Image; import app.mealsmadeeasy.api.recipe.Recipe; +import app.mealsmadeeasy.api.recipe.body.RecipeUpdateBody; +import lombok.Builder; +import lombok.Value; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; // For now, we cannot change slug after creation. // In the future, we may be able to have redirects from // old slugs to new slugs. +@Value +@Builder public class RecipeUpdateSpec { + @Value + @Builder public static class MainImageUpdateSpec { - - private String username; - private String filename; - - public String getUsername() { - return this.username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getFilename() { - return this.filename; - } - - public void setFilename(String filename) { - this.filename = filename; - } - + String username; + String filename; } - private String title; - private @Nullable Integer preparationTime; - private @Nullable Integer cookingTime; - private @Nullable Integer totalTime; - private String rawText; - private boolean isPublic; - private @Nullable MainImageUpdateSpec mainImage; - - public RecipeUpdateSpec() {} - - /** - * Convenience constructor for testing purposes. - * - * @param recipe the Recipe to copy from - */ - public RecipeUpdateSpec(Recipe recipe) { - this.title = recipe.getTitle(); - this.preparationTime = recipe.getPreparationTime(); - this.cookingTime = recipe.getCookingTime(); - this.totalTime = recipe.getTotalTime(); - this.rawText = recipe.getRawText(); - this.isPublic = recipe.getIsPublic(); - final @Nullable Image mainImage = recipe.getMainImage(); + public static RecipeUpdateSpec from(RecipeUpdateBody body) { + final var b = RecipeUpdateSpec.builder() + .title(body.getTitle()) + .preparationTime(body.getPreparationTime()) + .cookingTime(body.getCookingTime()) + .totalTime(body.getTotalTime()) + .rawText(body.getRawText()) + .isPublic(body.getIsPublic()); + final @Nullable RecipeUpdateBody.MainImageUpdateBody mainImage = body.getMainImage(); if (mainImage != null) { - this.mainImage = new MainImageUpdateSpec(); - this.mainImage.setUsername(mainImage.getOwner().getUsername()); - this.mainImage.setFilename(mainImage.getUserFilename()); + b.mainImage( + MainImageUpdateSpec.builder() + .username(mainImage.getUsername()) + .filename(mainImage.getFilename()) + .build() + ); } + return b.build(); } - public @Nullable String getTitle() { - return this.title; + // For testing convenience only. + @ApiStatus.Internal + public static RecipeUpdateSpec.RecipeUpdateSpecBuilder fromRecipeToBuilder(Recipe recipe) { + final var b = RecipeUpdateSpec.builder() + .title(recipe.getTitle()) + .preparationTime(recipe.getPreparationTime()) + .cookingTime(recipe.getCookingTime()) + .totalTime(recipe.getTotalTime()) + .rawText(recipe.getRawText()) + .isPublic(recipe.getIsPublic()); + final @Nullable Image mainImage = recipe.getMainImage(); + if (recipe.getMainImage() != null) { + b.mainImage(MainImageUpdateSpec.builder() + .username(mainImage.getOwner().getUsername()) + .filename(mainImage.getUserFilename()) + .build() + ); + } + return b; } - public void setTitle(@Nullable String title) { - this.title = title; - } - - public @Nullable Integer getPreparationTime() { - return this.preparationTime; - } - - public void setPreparationTime(@Nullable Integer preparationTime) { - this.preparationTime = preparationTime; - } - - public @Nullable Integer getCookingTime() { - return this.cookingTime; - } - - public void setCookingTime(@Nullable Integer cookingTime) { - this.cookingTime = cookingTime; - } - - public @Nullable Integer getTotalTime() { - return this.totalTime; - } - - public void setTotalTime(@Nullable Integer totalTime) { - this.totalTime = totalTime; - } - - public @Nullable String getRawText() { - return this.rawText; - } - - public void setRawText(@Nullable String rawText) { - this.rawText = rawText; - } - - public boolean getIsPublic() { - return this.isPublic; - } - - public void setIsPublic(boolean isPublic) { - this.isPublic = isPublic; - } - - public @Nullable MainImageUpdateSpec getMainImage() { - return this.mainImage; - } - - public void setMainImage(@Nullable MainImageUpdateSpec mainImage) { - this.mainImage = mainImage; - } + String title; + @Nullable Integer preparationTime; + @Nullable Integer cookingTime; + @Nullable Integer totalTime; + String rawText; + Boolean isPublic; + @Nullable MainImageUpdateSpec mainImage; } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStar.java b/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStar.java index 2437a97..28b7dea 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStar.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStar.java @@ -8,7 +8,7 @@ import lombok.Data; import java.time.OffsetDateTime; -@Entity(name = "RecipeStar") +@Entity @Table(name = "recipe_star") @Data public final class RecipeStar { diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarId.java b/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarId.java index b3536db..994e393 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarId.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarId.java @@ -2,10 +2,10 @@ package app.mealsmadeeasy.api.recipe.star; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; - -import java.util.Objects; +import lombok.Data; @Embeddable +@Data public class RecipeStarId { @Column(name = "owner_id", nullable = false) @@ -14,39 +14,4 @@ public class RecipeStarId { @Column(name = "recipe_id", nullable = false) private Integer recipeId; - public Integer getOwnerId() { - return this.ownerId; - } - - public void getOwnerId(Integer ownerId) { - this.ownerId = ownerId; - } - - public Integer getRecipeId() { - return this.recipeId; - } - - public void setRecipeId(Integer recipeId) { - this.recipeId = recipeId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o instanceof RecipeStarId other) { - return this.recipeId.equals(other.recipeId) && this.ownerId.equals(other.ownerId); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(this.recipeId, this.ownerId); - } - - @Override - public String toString() { - return "RecipeStarId(" + this.recipeId + ", " + this.ownerId + ")"; - } - } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceImpl.java b/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceImpl.java index 6f9d441..2a2162d 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceImpl.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/star/RecipeStarServiceImpl.java @@ -25,7 +25,7 @@ public class RecipeStarServiceImpl implements RecipeStarService { final RecipeStar draft = new RecipeStar(); final RecipeStarId id = new RecipeStarId(); id.setRecipeId(recipeId); - id.getOwnerId(ownerId); + id.setOwnerId(ownerId); draft.setId(id); draft.setTimestamp(OffsetDateTime.now()); return this.recipeStarRepository.save(draft); diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/view/FullRecipeView.java b/src/main/java/app/mealsmadeeasy/api/recipe/view/FullRecipeView.java index d389411..57e8105 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/view/FullRecipeView.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/view/FullRecipeView.java @@ -4,11 +4,14 @@ import app.mealsmadeeasy.api.image.view.ImageView; import app.mealsmadeeasy.api.recipe.Recipe; import app.mealsmadeeasy.api.user.view.UserInfoView; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; +import lombok.Builder; +import lombok.Value; import org.jetbrains.annotations.Nullable; import java.time.OffsetDateTime; +@Value +@Builder public class FullRecipeView { public static FullRecipeView from( @@ -19,162 +22,46 @@ public class FullRecipeView { int viewerCount, @Nullable ImageView mainImage ) { - final FullRecipeView view = new FullRecipeView(); - view.setId(recipe.getId()); - view.setCreated(recipe.getCreated()); - view.setModified(recipe.getModified()); - view.setSlug(recipe.getSlug()); - view.setTitle(recipe.getTitle()); - view.setPreparationTime(recipe.getPreparationTime()); - view.setCookingTime(recipe.getCookingTime()); - view.setTotalTime(recipe.getTotalTime()); - view.setText(renderedText); + final var b = FullRecipeView.builder() + .id(recipe.getId()) + .created(recipe.getCreated()) + .modified(recipe.getModified()) + .slug(recipe.getSlug()) + .title(recipe.getTitle()) + .preparationTime(recipe.getPreparationTime()) + .cookingTime(recipe.getCookingTime()) + .totalTime(recipe.getTotalTime()) + .text(renderedText) + .owner(UserInfoView.from(recipe.getOwner())) + .starCount(starCount) + .viewerCount(viewerCount) + .mainImage(mainImage) + .isPublic(recipe.getIsPublic()); if (includeRawText) { - view.setRawText(recipe.getRawText()); + b.rawText(recipe.getRawText()); } - view.setOwner(UserInfoView.from(recipe.getOwner())); - view.setStarCount(starCount); - view.setViewerCount(viewerCount); - view.setMainImage(mainImage); - view.setIsPublic(recipe.getIsPublic()); - return view; + return b.build(); } - private long id; - private OffsetDateTime created; - private @Nullable OffsetDateTime modified; - private String slug; - private String title; - private @Nullable Integer preparationTime; - private @Nullable Integer cookingTime; - private @Nullable Integer totalTime; - private String text; - private @Nullable String rawText; - private UserInfoView owner; - private int starCount; - private int viewerCount; - private @Nullable ImageView mainImage; - private boolean isPublic; + Integer id; + OffsetDateTime created; + @Nullable OffsetDateTime modified; + String slug; + String title; + @Nullable Integer preparationTime; + @Nullable Integer cookingTime; + @Nullable Integer totalTime; + String text; + @Nullable String rawText; + UserInfoView owner; + int starCount; + int viewerCount; + @Nullable ImageView mainImage; + boolean isPublic; - public long getId() { - return this.id; - } - - public void setId(long id) { - this.id = id; - } - - public OffsetDateTime getCreated() { - return this.created; - } - - public void setCreated(OffsetDateTime created) { - this.created = created; - } - - public @Nullable OffsetDateTime getModified() { - return this.modified; - } - - public void setModified(@Nullable OffsetDateTime modified) { - this.modified = modified; - } - - public String getSlug() { - return this.slug; - } - - public void setSlug(String slug) { - this.slug = slug; - } - - public String getTitle() { - return this.title; - } - - public void setTitle(String title) { - this.title = title; - } - - public @Nullable Integer getPreparationTime() { - return this.preparationTime; - } - - public void setPreparationTime(@Nullable Integer preparationTime) { - this.preparationTime = preparationTime; - } - - public @Nullable Integer getCookingTime() { - return this.cookingTime; - } - - public void setCookingTime(@Nullable Integer cookingTime) { - this.cookingTime = cookingTime; - } - - public @Nullable Integer getTotalTime() { - return this.totalTime; - } - - public void setTotalTime(@Nullable Integer totalTime) { - this.totalTime = totalTime; - } - - public @Nullable String getText() { - return this.text; - } - - public void setText(String text) { - this.text = text; - } - - @JsonInclude(Include.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_NULL) public @Nullable String getRawText() { return this.rawText; } - public void setRawText(@Nullable String rawText) { - this.rawText = rawText; - } - - public UserInfoView getOwner() { - return this.owner; - } - - public void setOwner(UserInfoView owner) { - this.owner = owner; - } - - public int getStarCount() { - return this.starCount; - } - - public void setStarCount(int starCount) { - this.starCount = starCount; - } - - public int getViewerCount() { - return this.viewerCount; - } - - public void setViewerCount(int viewerCount) { - this.viewerCount = viewerCount; - } - - public @Nullable ImageView getMainImage() { - return this.mainImage; - } - - public void setMainImage(@Nullable ImageView mainImage) { - this.mainImage = mainImage; - } - - public boolean getIsPublic() { - return this.isPublic; - } - - public void setIsPublic(boolean isPublic) { - this.isPublic = isPublic; - } - } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeExceptionView.java b/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeExceptionView.java index dc7f5b3..a5a51d3 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeExceptionView.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeExceptionView.java @@ -1,5 +1,8 @@ package app.mealsmadeeasy.api.recipe.view; +import lombok.Getter; + +@Getter public final class RecipeExceptionView { private final String type; @@ -10,12 +13,4 @@ public final class RecipeExceptionView { this.message = message; } - public String getType() { - return this.type; - } - - public String getMessage() { - return this.message; - } - } diff --git a/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeInfoView.java b/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeInfoView.java index 2f2e2e7..fe9c6c9 100644 --- a/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeInfoView.java +++ b/src/main/java/app/mealsmadeeasy/api/recipe/view/RecipeInfoView.java @@ -3,42 +3,44 @@ package app.mealsmadeeasy.api.recipe.view; import app.mealsmadeeasy.api.image.view.ImageView; import app.mealsmadeeasy.api.recipe.Recipe; import app.mealsmadeeasy.api.user.view.UserInfoView; -import lombok.Data; +import lombok.Builder; +import lombok.Value; import org.jetbrains.annotations.Nullable; import java.time.OffsetDateTime; -@Data +@Value +@Builder public class RecipeInfoView { public static RecipeInfoView from(Recipe recipe, int starCount, @Nullable ImageView mainImage) { - final RecipeInfoView view = new RecipeInfoView(); - view.setId(recipe.getId()); - view.setCreated(recipe.getCreated()); - view.setModified(recipe.getModified()); - view.setSlug(recipe.getSlug()); - view.setTitle(recipe.getTitle()); - view.setPreparationTime(recipe.getPreparationTime()); - view.setCookingTime(recipe.getCookingTime()); - view.setTotalTime(recipe.getTotalTime()); - view.setOwner(UserInfoView.from(recipe.getOwner())); - view.setPublic(recipe.getIsPublic()); - view.setStarCount(starCount); - view.setMainImage(mainImage); - return view; + return RecipeInfoView.builder() + .id(recipe.getId()) + .created(recipe.getCreated()) + .modified(recipe.getModified()) + .slug(recipe.getSlug()) + .title(recipe.getTitle()) + .preparationTime(recipe.getPreparationTime()) + .cookingTime(recipe.getCookingTime()) + .totalTime(recipe.getTotalTime()) + .owner(UserInfoView.from(recipe.getOwner())) + .isPublic(recipe.getIsPublic()) + .starCount(starCount) + .mainImage(mainImage) + .build(); } - private Integer id; - private OffsetDateTime created; - private OffsetDateTime modified; - private String slug; - private String title; - private @Nullable Integer preparationTime; - private @Nullable Integer cookingTime; - private @Nullable Integer totalTime; - private UserInfoView owner; - private boolean isPublic; - private int starCount; - private @Nullable ImageView mainImage; + Integer id; + OffsetDateTime created; + OffsetDateTime modified; + String slug; + String title; + @Nullable Integer preparationTime; + @Nullable Integer cookingTime; + @Nullable Integer totalTime; + UserInfoView owner; + boolean isPublic; + int starCount; + @Nullable ImageView mainImage; }