From 708ac4a0602afad549d4486f1d33d0b93cf72712 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 16:28:31 +0900 Subject: [PATCH 01/16] :sparkles: implement recipe create api --- .../recipe/controller/RecipeController.java | 11 ++++++++ .../net/pengcook/recipe/domain/Recipe.java | 12 +++++++++ .../pengcook/recipe/dto/RecipeRequest.java | 18 +++++++++++++ .../pengcook/recipe/dto/RecipeResponse.java | 10 ++++++++ .../recipe/service/RecipeService.java | 25 +++++++++++++++++++ .../category/service/CategoryServiceTest.java | 2 +- .../recipe/service/RecipeServiceTest.java | 7 +++++- 7 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java create mode 100644 backend/src/main/java/net/pengcook/recipe/dto/RecipeResponse.java diff --git a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java index cb3d0f7f..565fc4cb 100644 --- a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java +++ b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java @@ -2,13 +2,19 @@ import java.util.List; import lombok.RequiredArgsConstructor; +import net.pengcook.authentication.domain.UserInfo; +import net.pengcook.authentication.resolver.LoginUser; import net.pengcook.category.dto.RecipeOfCategoryRequest; import net.pengcook.recipe.dto.MainRecipeResponse; +import net.pengcook.recipe.dto.RecipeRequest; +import net.pengcook.recipe.dto.RecipeResponse; import net.pengcook.recipe.dto.RecipeStepResponse; import net.pengcook.recipe.service.RecipeService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -25,6 +31,11 @@ public List readRecipes(@RequestParam int pageNumber, @Reque return recipeService.readRecipes(pageNumber, pageSize); } + @PostMapping + public RecipeResponse createRecipe(@LoginUser UserInfo userInfo, @RequestBody RecipeRequest recipeRequest) { + return recipeService.createRecipe(userInfo, recipeRequest); + } + @GetMapping("/{id}/steps") public List readRecipeSteps(@PathVariable long id) { return recipeService.readRecipeSteps(id); diff --git a/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java b/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java index 46b9f53d..7a5108d0 100644 --- a/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java +++ b/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java @@ -45,4 +45,16 @@ public class Recipe { @Column(nullable = true) private String description; + + public Recipe( + String title, + User author, + LocalTime cookingTime, + String thumbnail, + int difficulty, + int likeCount, + String description + ) { + this(0L, title, author, cookingTime, thumbnail, difficulty, likeCount, description); + } } diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java new file mode 100644 index 00000000..87a3f94a --- /dev/null +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java @@ -0,0 +1,18 @@ +package net.pengcook.recipe.dto; + +import jakarta.annotation.Nullable; +import java.time.LocalTime; +import java.util.List; +import net.pengcook.ingredient.dto.IngredientCreateRequest; + +public record RecipeRequest( + String title, + LocalTime cookingTime, + String thumbnail, + int difficulty, + int likeCount, + String description, + List categories, + List ingredients +) { +} diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeResponse.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeResponse.java new file mode 100644 index 00000000..af0c190f --- /dev/null +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeResponse.java @@ -0,0 +1,10 @@ +package net.pengcook.recipe.dto; + +import net.pengcook.recipe.domain.Recipe; + +public record RecipeResponse(long recipeId) { + + public RecipeResponse(Recipe savedRecipe) { + this(savedRecipe.getId()); + } +} diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index b511f033..1d315291 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -5,17 +5,25 @@ import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import net.pengcook.authentication.domain.UserInfo; import net.pengcook.category.dto.RecipeOfCategoryRequest; import net.pengcook.category.repository.CategoryRecipeRepository; +import net.pengcook.category.service.CategoryService; +import net.pengcook.ingredient.service.IngredientService; +import net.pengcook.recipe.domain.Recipe; import net.pengcook.recipe.domain.RecipeStep; import net.pengcook.recipe.dto.AuthorResponse; import net.pengcook.recipe.dto.CategoryResponse; import net.pengcook.recipe.dto.IngredientResponse; import net.pengcook.recipe.dto.MainRecipeResponse; import net.pengcook.recipe.dto.RecipeDataResponse; +import net.pengcook.recipe.dto.RecipeRequest; +import net.pengcook.recipe.dto.RecipeResponse; import net.pengcook.recipe.dto.RecipeStepResponse; import net.pengcook.recipe.repository.RecipeRepository; import net.pengcook.recipe.repository.RecipeStepRepository; +import net.pengcook.user.domain.User; +import net.pengcook.user.repository.UserRepository; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -27,6 +35,10 @@ public class RecipeService { private final RecipeRepository recipeRepository; private final RecipeStepRepository recipeStepRepository; private final CategoryRecipeRepository categoryRecipeRepository; + private final UserRepository userRepository; + + private final CategoryService categoryService; + private final IngredientService ingredientService; public List readRecipes(int pageNumber, int pageSize) { Pageable pageable = PageRequest.of(pageNumber, pageSize); @@ -36,6 +48,19 @@ public List readRecipes(int pageNumber, int pageSize) { return convertToMainRecipeResponses(recipeDataResponses); } + public RecipeResponse createRecipe(UserInfo userInfo, RecipeRequest recipeRequest) { + User author = userRepository.findById(userInfo.getId()).orElseThrow(); + Recipe recipe = new Recipe(recipeRequest.title(), author, recipeRequest.cookingTime(), + recipeRequest.thumbnail(), + recipeRequest.difficulty(), recipeRequest.likeCount(), recipeRequest.description()); + + Recipe savedRecipe = recipeRepository.save(recipe); + categoryService.saveCategories(savedRecipe, recipeRequest.categories()); + ingredientService.register(recipeRequest.ingredients(), savedRecipe); + + return new RecipeResponse(savedRecipe); + } + public List readRecipeSteps(long id) { List recipeSteps = recipeStepRepository.findAllByRecipeIdOrderBySequence(id); return convertToRecipeStepResponses(recipeSteps); diff --git a/backend/src/test/java/net/pengcook/category/service/CategoryServiceTest.java b/backend/src/test/java/net/pengcook/category/service/CategoryServiceTest.java index 3bb4cfad..0c57cce2 100644 --- a/backend/src/test/java/net/pengcook/category/service/CategoryServiceTest.java +++ b/backend/src/test/java/net/pengcook/category/service/CategoryServiceTest.java @@ -18,7 +18,7 @@ import org.springframework.test.context.jdbc.Sql; @DataJpaTest -@Import({CategoryService.class, RecipeService.class}) +@Import(CategoryService.class) @Sql(scripts = "/data/category.sql") class CategoryServiceTest { diff --git a/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java b/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java index 6fe915f1..28498940 100644 --- a/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java +++ b/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java @@ -7,6 +7,10 @@ import java.util.List; import java.util.stream.Stream; import net.pengcook.category.dto.RecipeOfCategoryRequest; +import net.pengcook.category.service.CategoryService; +import net.pengcook.ingredient.service.IngredientRecipeService; +import net.pengcook.ingredient.service.IngredientService; +import net.pengcook.ingredient.service.IngredientSubstitutionService; import net.pengcook.recipe.dto.MainRecipeResponse; import net.pengcook.recipe.dto.RecipeStepResponse; import org.junit.jupiter.api.DisplayName; @@ -21,7 +25,8 @@ import org.springframework.test.context.jdbc.Sql; @DataJpaTest -@Import(RecipeService.class) +@Import({RecipeService.class, CategoryService.class, IngredientService.class, IngredientRecipeService.class, + IngredientSubstitutionService.class}) @Sql(value = "/data/recipe.sql") class RecipeServiceTest { From da5733734e29041b9121e986012927653cb85ba1 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 16:39:28 +0900 Subject: [PATCH 02/16] :sparkles: implement recipe step create api --- .../recipe/controller/RecipeController.java | 7 +++++++ .../net/pengcook/recipe/domain/RecipeStep.java | 7 +++++++ .../pengcook/recipe/dto/RecipeStepRequest.java | 6 ++++++ .../pengcook/recipe/service/RecipeService.java | 15 +++++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java diff --git a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java index 565fc4cb..e0f864a0 100644 --- a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java +++ b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java @@ -8,6 +8,7 @@ import net.pengcook.recipe.dto.MainRecipeResponse; import net.pengcook.recipe.dto.RecipeRequest; import net.pengcook.recipe.dto.RecipeResponse; +import net.pengcook.recipe.dto.RecipeStepRequest; import net.pengcook.recipe.dto.RecipeStepResponse; import net.pengcook.recipe.service.RecipeService; import org.springframework.web.bind.annotation.GetMapping; @@ -41,6 +42,12 @@ public List readRecipeSteps(@PathVariable long id) { return recipeService.readRecipeSteps(id); } + @PostMapping("/{recipeId}/steps") + public RecipeStepResponse createRecipeStep(@PathVariable long recipeId, + @RequestBody RecipeStepRequest recipeStepRequest) { + return recipeService.createRecipeStep(recipeId, recipeStepRequest); + } + @GetMapping("/search") public List readRecipesOfCategory(@ModelAttribute RecipeOfCategoryRequest request) { return recipeService.readRecipesOfCategory(request); diff --git a/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java b/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java index 3468f297..9f9fbdf7 100644 --- a/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java +++ b/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java @@ -6,6 +6,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import java.time.LocalTime; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -31,7 +32,13 @@ public class RecipeStep { private int sequence; + private LocalTime cookingTime; + public long recipeId() { return recipe.getId(); } + + public RecipeStep(Recipe recipe, String image, String description, int sequence, LocalTime cookingTime) { + this(0L, recipe, image, description, sequence, cookingTime); + } } diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java new file mode 100644 index 00000000..8cf30a7a --- /dev/null +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java @@ -0,0 +1,6 @@ +package net.pengcook.recipe.dto; + +import java.time.LocalTime; + +public record RecipeStepRequest(String image, String description, int sequence, LocalTime cookingTime) { +} diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index 1d315291..7e68fa47 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -19,13 +19,16 @@ import net.pengcook.recipe.dto.RecipeDataResponse; import net.pengcook.recipe.dto.RecipeRequest; import net.pengcook.recipe.dto.RecipeResponse; +import net.pengcook.recipe.dto.RecipeStepRequest; import net.pengcook.recipe.dto.RecipeStepResponse; +import net.pengcook.recipe.exception.RecipeException; import net.pengcook.recipe.repository.RecipeRepository; import net.pengcook.recipe.repository.RecipeStepRepository; import net.pengcook.user.domain.User; import net.pengcook.user.repository.UserRepository; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @Service @@ -66,6 +69,18 @@ public List readRecipeSteps(long id) { return convertToRecipeStepResponses(recipeSteps); } + public RecipeStepResponse createRecipeStep(long recipeId, RecipeStepRequest recipeStepRequest) { + Recipe recipe = recipeRepository.findById(recipeId) + .orElseThrow(() -> new RecipeException(HttpStatus.NO_CONTENT, "해당되는 레시피가 없습니다.")); + + RecipeStep recipeStep = new RecipeStep(recipe, recipeStepRequest.image(), recipeStepRequest.description(), + recipeStepRequest.sequence(), recipeStepRequest.cookingTime()); + + RecipeStep savedRecipeStep = recipeStepRepository.save(recipeStep); + + return new RecipeStepResponse(savedRecipeStep); + } + public List readRecipesOfCategory(RecipeOfCategoryRequest request) { String categoryName = request.category(); Pageable pageable = PageRequest.of(request.pageNumber(), request.pageSize()); From 10b3a9780903e6f6c842cb485fc33bae14e04ca5 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 16:46:54 +0900 Subject: [PATCH 03/16] :sparkles: implement recipe step retrieve api --- .../net/pengcook/recipe/controller/RecipeController.java | 5 +++++ .../pengcook/recipe/repository/RecipeStepRepository.java | 3 +++ .../java/net/pengcook/recipe/service/RecipeService.java | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java index e0f864a0..066b4010 100644 --- a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java +++ b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java @@ -42,6 +42,11 @@ public List readRecipeSteps(@PathVariable long id) { return recipeService.readRecipeSteps(id); } + @GetMapping("/{recipeId}/steps/{sequence}") + public RecipeStepResponse readRecipeStep(@PathVariable long recipeId, @PathVariable long sequence) { + return recipeService.readRecipeStep(recipeId, sequence); + } + @PostMapping("/{recipeId}/steps") public RecipeStepResponse createRecipeStep(@PathVariable long recipeId, @RequestBody RecipeStepRequest recipeStepRequest) { diff --git a/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java b/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java index da082f2d..550d56a6 100644 --- a/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java +++ b/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java @@ -1,10 +1,13 @@ package net.pengcook.recipe.repository; import java.util.List; +import java.util.Optional; import net.pengcook.recipe.domain.RecipeStep; import org.springframework.data.jpa.repository.JpaRepository; public interface RecipeStepRepository extends JpaRepository { List findAllByRecipeIdOrderBySequence(long id); + + Optional findByRecipeIdAndSequence(long recipeId, long sequence); } diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index 7e68fa47..ebb18c1a 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -69,6 +69,13 @@ public List readRecipeSteps(long id) { return convertToRecipeStepResponses(recipeSteps); } + public RecipeStepResponse readRecipeStep(long recipeId, long sequence) { + RecipeStep recipeStep = recipeStepRepository.findByRecipeIdAndSequence(recipeId, sequence) + .orElseThrow(() -> new RecipeException(HttpStatus.NO_CONTENT, "해당되는 레시피 스텝 정보가 없습니다.")); + + return new RecipeStepResponse(recipeStep); + } + public RecipeStepResponse createRecipeStep(long recipeId, RecipeStepRequest recipeStepRequest) { Recipe recipe = recipeRepository.findById(recipeId) .orElseThrow(() -> new RecipeException(HttpStatus.NO_CONTENT, "해당되는 레시피가 없습니다.")); From 19a20a29648f99f37c6d509d0411cda65fb8194d Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 17:22:59 +0900 Subject: [PATCH 04/16] :recycle: remove like count from recipe request --- backend/src/main/java/net/pengcook/recipe/domain/Recipe.java | 3 +-- .../src/main/java/net/pengcook/recipe/dto/RecipeRequest.java | 1 - .../main/java/net/pengcook/recipe/service/RecipeService.java | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java b/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java index 7a5108d0..71ef2cf0 100644 --- a/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java +++ b/backend/src/main/java/net/pengcook/recipe/domain/Recipe.java @@ -52,9 +52,8 @@ public Recipe( LocalTime cookingTime, String thumbnail, int difficulty, - int likeCount, String description ) { - this(0L, title, author, cookingTime, thumbnail, difficulty, likeCount, description); + this(0L, title, author, cookingTime, thumbnail, difficulty, 0, description); } } diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java index 87a3f94a..25e4eeeb 100644 --- a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java @@ -10,7 +10,6 @@ public record RecipeRequest( LocalTime cookingTime, String thumbnail, int difficulty, - int likeCount, String description, List categories, List ingredients diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index ebb18c1a..0c410ac6 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -54,8 +54,7 @@ public List readRecipes(int pageNumber, int pageSize) { public RecipeResponse createRecipe(UserInfo userInfo, RecipeRequest recipeRequest) { User author = userRepository.findById(userInfo.getId()).orElseThrow(); Recipe recipe = new Recipe(recipeRequest.title(), author, recipeRequest.cookingTime(), - recipeRequest.thumbnail(), - recipeRequest.difficulty(), recipeRequest.likeCount(), recipeRequest.description()); + recipeRequest.thumbnail(), recipeRequest.difficulty(), recipeRequest.description()); Recipe savedRecipe = recipeRepository.save(recipe); categoryService.saveCategories(savedRecipe, recipeRequest.categories()); From 9baa0e4f73771a3ef6cf241d922a368d7fdf2a1d Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 17:25:14 +0900 Subject: [PATCH 05/16] :recycle: change type of cooking time --- .../src/main/java/net/pengcook/recipe/dto/RecipeRequest.java | 2 +- .../main/java/net/pengcook/recipe/dto/RecipeStepRequest.java | 4 +--- .../main/java/net/pengcook/recipe/service/RecipeService.java | 5 +++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java index 25e4eeeb..c22c92d8 100644 --- a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java @@ -7,7 +7,7 @@ public record RecipeRequest( String title, - LocalTime cookingTime, + String cookingTime, String thumbnail, int difficulty, String description, diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java index 8cf30a7a..5e5dd959 100644 --- a/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java @@ -1,6 +1,4 @@ package net.pengcook.recipe.dto; -import java.time.LocalTime; - -public record RecipeStepRequest(String image, String description, int sequence, LocalTime cookingTime) { +public record RecipeStepRequest(String image, String description, int sequence, String cookingTime) { } diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index 0c410ac6..d64e6cd8 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -1,5 +1,6 @@ package net.pengcook.recipe.service; +import java.time.LocalTime; import java.util.Collection; import java.util.Comparator; import java.util.List; @@ -53,7 +54,7 @@ public List readRecipes(int pageNumber, int pageSize) { public RecipeResponse createRecipe(UserInfo userInfo, RecipeRequest recipeRequest) { User author = userRepository.findById(userInfo.getId()).orElseThrow(); - Recipe recipe = new Recipe(recipeRequest.title(), author, recipeRequest.cookingTime(), + Recipe recipe = new Recipe(recipeRequest.title(), author, LocalTime.parse(recipeRequest.cookingTime()), recipeRequest.thumbnail(), recipeRequest.difficulty(), recipeRequest.description()); Recipe savedRecipe = recipeRepository.save(recipe); @@ -80,7 +81,7 @@ public RecipeStepResponse createRecipeStep(long recipeId, RecipeStepRequest reci .orElseThrow(() -> new RecipeException(HttpStatus.NO_CONTENT, "해당되는 레시피가 없습니다.")); RecipeStep recipeStep = new RecipeStep(recipe, recipeStepRequest.image(), recipeStepRequest.description(), - recipeStepRequest.sequence(), recipeStepRequest.cookingTime()); + recipeStepRequest.sequence(), LocalTime.parse(recipeStepRequest.cookingTime())); RecipeStep savedRecipeStep = recipeStepRepository.save(recipeStep); From 1e19d15dbd0243272cb13253c9b20a01bfdd29ad Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 17:25:35 +0900 Subject: [PATCH 06/16] :recycle: change http status of create api --- .../java/net/pengcook/recipe/controller/RecipeController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java index 066b4010..4a82d273 100644 --- a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java +++ b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java @@ -11,6 +11,7 @@ import net.pengcook.recipe.dto.RecipeStepRequest; import net.pengcook.recipe.dto.RecipeStepResponse; import net.pengcook.recipe.service.RecipeService; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; @@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RestController @@ -33,6 +35,7 @@ public List readRecipes(@RequestParam int pageNumber, @Reque } @PostMapping + @ResponseStatus(HttpStatus.CREATED) public RecipeResponse createRecipe(@LoginUser UserInfo userInfo, @RequestBody RecipeRequest recipeRequest) { return recipeService.createRecipe(userInfo, recipeRequest); } @@ -48,6 +51,7 @@ public RecipeStepResponse readRecipeStep(@PathVariable long recipeId, @PathVaria } @PostMapping("/{recipeId}/steps") + @ResponseStatus(HttpStatus.CREATED) public RecipeStepResponse createRecipeStep(@PathVariable long recipeId, @RequestBody RecipeStepRequest recipeStepRequest) { return recipeService.createRecipeStep(recipeId, recipeStepRequest); From b8869dd6958069bb84608d6f15f7c9610bfbac52 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 17:28:07 +0900 Subject: [PATCH 07/16] :white_check_mark: add create recipe test --- .../controller/RecipeControllerTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java index 59a72506..f6f4ccd0 100644 --- a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java +++ b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java @@ -3,17 +3,26 @@ import static com.epages.restdocs.apispec.RestAssuredRestDocumentationWrapper.document; import static org.hamcrest.Matchers.is; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import java.util.List; import net.pengcook.RestDocsSetting; +import net.pengcook.authentication.annotation.WithLoginUser; +import net.pengcook.authentication.annotation.WithLoginUserTest; +import net.pengcook.ingredient.domain.Requirement; +import net.pengcook.ingredient.dto.IngredientCreateRequest; +import net.pengcook.recipe.dto.RecipeRequest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.test.context.jdbc.Sql; +@WithLoginUserTest @Sql(value = "/data/recipe.sql") class RecipeControllerTest extends RestDocsSetting { @@ -57,6 +66,52 @@ void readRecipes() { .body("size()", is(3)); } + @Test + @WithLoginUser(email = "loki@pengcook.net") + @DisplayName("새로운 레시피를 생성한다.") + void createRecipe() { + List categories = List.of("Dessert", "NewCategory"); + List substitutions = List.of("Water", "Orange"); + List ingredients = List.of( + new IngredientCreateRequest("Apple", Requirement.REQUIRED, substitutions), + new IngredientCreateRequest("WaterMelon", Requirement.OPTIONAL, null) + ); + RecipeRequest recipeRequest = new RecipeRequest( + "새로운 레시피 제목", + "00:30:00", + "레시피 썸네일.jpg", + 4, + "새로운 레시피 설명", + categories, + ingredients + ); + + RestAssured.given(spec).log().all() + .filter(document(DEFAULT_RESTDOCS_PATH, + "새로운 레시피 개요를 등록합니다.", + "신규 레시피 생성 API", + requestFields( + fieldWithPath("title").description("레시피 제목"), + fieldWithPath("cookingTime").description("조리 시간"), + fieldWithPath("thumbnail").description("썸네일 이미지"), + fieldWithPath("difficulty").description("난이도"), + fieldWithPath("description").description("레시피 설명"), + fieldWithPath("categories").description("카테고리 목록"), + fieldWithPath("ingredients[]").description("재료 목록"), + fieldWithPath("ingredients[].name").description("재료 이름"), + fieldWithPath("ingredients[].requirement").description("재료 필수 여부"), + fieldWithPath("ingredients[].substitutions").description("대체 재료 목록").optional() + ), responseFields( + fieldWithPath("recipeId").description("생성된 레시피 아이디") + ))) + .contentType(ContentType.JSON) + .body(recipeRequest) + .when().post("/api/recipes") + .then().log().all() + .statusCode(201) + .body("recipeId", is(16)); + } + @Test @DisplayName("레시피 상세 스텝을 조회한다.") void readRecipeSteps() { From 0026c08a53e611df44fa6f53fd9cb69985a01521 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 17:52:21 +0900 Subject: [PATCH 08/16] :white_check_mark: add test for retrieving specific recipe step --- .../controller/RecipeControllerTest.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java index f6f4ccd0..2b9e0ba3 100644 --- a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java +++ b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java @@ -136,6 +136,31 @@ void readRecipeSteps() { .body("size()", is(3)); } + @Test + @DisplayName("특정 레시피의 특정 스텝을 조회한다.") + void readRecipeStep() { + RestAssured.given(spec).log().all() + .filter(document(DEFAULT_RESTDOCS_PATH, + "특정 레시피의 특정 스텝을 조회합니다.", + "특정 레시피 특정 스텝 조회 API", + pathParameters( + parameterWithName("recipeId").description("조회할 레시피 아이디"), + parameterWithName("sequence").description("조회할 스텝 순서") + ), + responseFields( + fieldWithPath("id").description("레시피 스텝 아이디"), + fieldWithPath("recipeId").description("레시피 아이디"), + fieldWithPath("image").description("레시피 스텝 이미지"), + fieldWithPath("description").description("레시피 스텝 설명"), + fieldWithPath("sequence").description("레시피 스텝 순서") + ))) + .when() + .get("/api/recipes/{recipeId}/steps/{sequence}", 1L, 1L) + .then().log().all() + .statusCode(200) + .body("description", is("레시피1 설명1")); + } + @Test @DisplayName("카테고리별 레시피 개요 목록을 조회한다.") void readRecipesOfCategory() { From dbcffa74f361f9abfb5ca3de506e7ffb77aa4a47 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 18:47:28 +0900 Subject: [PATCH 09/16] :white_check_mark: add test for creating recipe step --- .../controller/RecipeControllerTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java index 2b9e0ba3..8fdd7969 100644 --- a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java +++ b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java @@ -18,6 +18,7 @@ import net.pengcook.ingredient.domain.Requirement; import net.pengcook.ingredient.dto.IngredientCreateRequest; import net.pengcook.recipe.dto.RecipeRequest; +import net.pengcook.recipe.dto.RecipeStepRequest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.test.context.jdbc.Sql; @@ -161,6 +162,39 @@ void readRecipeStep() { .body("description", is("레시피1 설명1")); } + @Test + @DisplayName("특정 레시피의 레시피 스텝을 생성한다.") + void createRecipeStep() { + RecipeStepRequest recipeStepRequest = new RecipeStepRequest("신규 스텝 이미지.jpg", "신규 스텝 설명", 4, "00:05:00"); + + RestAssured.given(spec).log().all() + .filter(document(DEFAULT_RESTDOCS_PATH, + "특정 레시피의 레시피 스텝을 생성합니다.", + "특정 레시피 레시피 스텝 생성 API", + pathParameters( + parameterWithName("recipeId").description("레시피 스텝을 추가할 레시피 아이디") + ), + requestFields( + fieldWithPath("image").description("레시피 스텝 이미지"), + fieldWithPath("description").description("레시피 스텝 설명"), + fieldWithPath("sequence").description("레시피 스텝 순서"), + fieldWithPath("cookingTime").description("레시피 스텝 소요시간") + ), + responseFields( + fieldWithPath("id").description("레시피 스텝 아이디"), + fieldWithPath("recipeId").description("레시피 아이디"), + fieldWithPath("image").description("레시피 스텝 이미지"), + fieldWithPath("description").description("레시피 스텝 설명"), + fieldWithPath("sequence").description("레시피 스텝 순서") + ))) + .contentType(ContentType.JSON) + .body(recipeStepRequest) + .when() + .post("/api/recipes/{recipeId}/steps", 1L) + .then().log().all() + .statusCode(201); + } + @Test @DisplayName("카테고리별 레시피 개요 목록을 조회한다.") void readRecipesOfCategory() { From 1cd5603e7afe9dff356fe79bf88df12f76b74b17 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 19:08:52 +0900 Subject: [PATCH 10/16] :white_check_mark: add recipe service test --- .../recipe/service/RecipeServiceTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java b/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java index 28498940..438e3b4b 100644 --- a/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java +++ b/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java @@ -6,12 +6,18 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Stream; +import net.pengcook.authentication.domain.UserInfo; import net.pengcook.category.dto.RecipeOfCategoryRequest; import net.pengcook.category.service.CategoryService; +import net.pengcook.ingredient.domain.Requirement; +import net.pengcook.ingredient.dto.IngredientCreateRequest; import net.pengcook.ingredient.service.IngredientRecipeService; import net.pengcook.ingredient.service.IngredientService; import net.pengcook.ingredient.service.IngredientSubstitutionService; import net.pengcook.recipe.dto.MainRecipeResponse; +import net.pengcook.recipe.dto.RecipeRequest; +import net.pengcook.recipe.dto.RecipeResponse; +import net.pengcook.recipe.dto.RecipeStepRequest; import net.pengcook.recipe.dto.RecipeStepResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -42,6 +48,32 @@ void readRecipes(int pageNumber, int pageSize, int expectedFirstRecipeId) { assertThat(mainRecipeResponses.getFirst().recipeId()).isEqualTo(expectedFirstRecipeId); } + @Test + @DisplayName("새로운 레시피를 생성한다.") + void createRecipe() { + UserInfo userInfo = new UserInfo(1L, "loki@pengcook.net"); + + List categories = List.of("Dessert", "NewCategory"); + List substitutions = List.of("Water", "Orange"); + List ingredients = List.of( + new IngredientCreateRequest("Apple", Requirement.REQUIRED, substitutions), + new IngredientCreateRequest("WaterMelon", Requirement.OPTIONAL, null) + ); + RecipeRequest recipeRequest = new RecipeRequest( + "새로운 레시피 제목", + "00:30:00", + "레시피 썸네일.jpg", + 4, + "새로운 레시피 설명", + categories, + ingredients + ); + + RecipeResponse recipe = recipeService.createRecipe(userInfo, recipeRequest); + + assertThat(recipe.recipeId()).isEqualTo(16L); + } + @Test @DisplayName("특정 레시피의 스텝을 sequence 순서로 조회한다.") void readRecipeSteps() { @@ -57,6 +89,31 @@ void readRecipeSteps() { assertThat(recipeStepResponses).isEqualTo(expectedRecipeStepResponses); } + @Test + @DisplayName("특정 레시피의 특정 레시피 스텝을 조회한다.") + void readRecipeStep() { + RecipeStepResponse recipeStepResponse = recipeService.readRecipeStep(1L, 1L); + + assertAll( + () -> assertThat(recipeStepResponse.recipeId()).isEqualTo(1L), + () -> assertThat(recipeStepResponse.description()).isEqualTo("레시피1 설명1") + ); + + } + + @Test + @DisplayName("특정 레시피의 레시피 스텝을 생성한다.") + void createRecipeStep() { + RecipeStepRequest recipeStepRequest = new RecipeStepRequest("새로운 스텝 이미지.jpg", "새로운 스텝 설명", 1, "00:05:00"); + + RecipeStepResponse recipeStepResponse = recipeService.createRecipeStep(2L, recipeStepRequest); + + assertAll( + () -> assertThat(recipeStepResponse.recipeId()).isEqualTo(2L), + () -> assertThat(recipeStepResponse.description()).isEqualTo("새로운 스텝 설명") + ); + } + @ParameterizedTest @MethodSource("provideParameters") @DisplayName("특정 카테고리의 레시피를 찾는다.") From ce57212dbec6ec28c67ebcc664ca354efd0941a9 Mon Sep 17 00:00:00 2001 From: hyun Date: Thu, 1 Aug 2024 19:17:52 +0900 Subject: [PATCH 11/16] :recycle: add transaction to recipe service --- .../main/java/net/pengcook/recipe/service/RecipeService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index d64e6cd8..8bcaab98 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -31,8 +31,10 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@Transactional @RequiredArgsConstructor public class RecipeService { From 9271e693edfef1ce27ddfed4f0b7b4b3923199b3 Mon Sep 17 00:00:00 2001 From: hyun Date: Fri, 2 Aug 2024 15:54:15 +0900 Subject: [PATCH 12/16] :recycle: add recipe exception for handling missing content --- .../pengcook/recipe/exception/NotFoundException.java | 10 ++++++++++ .../net/pengcook/recipe/service/RecipeService.java | 7 +++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/java/net/pengcook/recipe/exception/NotFoundException.java diff --git a/backend/src/main/java/net/pengcook/recipe/exception/NotFoundException.java b/backend/src/main/java/net/pengcook/recipe/exception/NotFoundException.java new file mode 100644 index 00000000..99b9052e --- /dev/null +++ b/backend/src/main/java/net/pengcook/recipe/exception/NotFoundException.java @@ -0,0 +1,10 @@ +package net.pengcook.recipe.exception; + +import org.springframework.http.HttpStatus; + +public class NotFoundException extends RecipeException { + + public NotFoundException(String message) { + super(HttpStatus.NOT_FOUND, message); + } +} diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index 8bcaab98..6a87d6de 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -22,14 +22,13 @@ import net.pengcook.recipe.dto.RecipeResponse; import net.pengcook.recipe.dto.RecipeStepRequest; import net.pengcook.recipe.dto.RecipeStepResponse; -import net.pengcook.recipe.exception.RecipeException; +import net.pengcook.recipe.exception.NotFoundException; import net.pengcook.recipe.repository.RecipeRepository; import net.pengcook.recipe.repository.RecipeStepRepository; import net.pengcook.user.domain.User; import net.pengcook.user.repository.UserRepository; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -73,14 +72,14 @@ public List readRecipeSteps(long id) { public RecipeStepResponse readRecipeStep(long recipeId, long sequence) { RecipeStep recipeStep = recipeStepRepository.findByRecipeIdAndSequence(recipeId, sequence) - .orElseThrow(() -> new RecipeException(HttpStatus.NO_CONTENT, "해당되는 레시피 스텝 정보가 없습니다.")); + .orElseThrow(() -> new NotFoundException("해당되는 레시피 스텝 정보가 없습니다.")); return new RecipeStepResponse(recipeStep); } public RecipeStepResponse createRecipeStep(long recipeId, RecipeStepRequest recipeStepRequest) { Recipe recipe = recipeRepository.findById(recipeId) - .orElseThrow(() -> new RecipeException(HttpStatus.NO_CONTENT, "해당되는 레시피가 없습니다.")); + .orElseThrow(() -> new NotFoundException("해당되는 레시피가 없습니다.")); RecipeStep recipeStep = new RecipeStep(recipe, recipeStepRequest.image(), recipeStepRequest.description(), recipeStepRequest.sequence(), LocalTime.parse(recipeStepRequest.cookingTime())); From 5bb02c78dd3b808c476bbf5f874fe281d310f8e9 Mon Sep 17 00:00:00 2001 From: hyun Date: Fri, 2 Aug 2024 16:04:57 +0900 Subject: [PATCH 13/16] :recycle: apply code conventions --- .../main/java/net/pengcook/recipe/domain/RecipeStep.java | 8 ++++---- .../main/java/net/pengcook/recipe/dto/RecipeRequest.java | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java b/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java index 9f9fbdf7..d5c116f7 100644 --- a/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java +++ b/backend/src/main/java/net/pengcook/recipe/domain/RecipeStep.java @@ -34,11 +34,11 @@ public class RecipeStep { private LocalTime cookingTime; - public long recipeId() { - return recipe.getId(); - } - public RecipeStep(Recipe recipe, String image, String description, int sequence, LocalTime cookingTime) { this(0L, recipe, image, description, sequence, cookingTime); } + + public long recipeId() { + return recipe.getId(); + } } diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java index c22c92d8..0603250c 100644 --- a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java @@ -1,7 +1,5 @@ package net.pengcook.recipe.dto; -import jakarta.annotation.Nullable; -import java.time.LocalTime; import java.util.List; import net.pengcook.ingredient.dto.IngredientCreateRequest; From 0e45cee9919ab36b06257153922fad8d906f2b76 Mon Sep 17 00:00:00 2001 From: hyun Date: Fri, 2 Aug 2024 17:38:57 +0900 Subject: [PATCH 14/16] :recycle: add recipe request validation --- .../net/pengcook/recipe/dto/RecipeRequest.java | 15 ++++++++------- .../pengcook/recipe/dto/RecipeStepRequest.java | 10 +++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java index 0603250c..35e4d08c 100644 --- a/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeRequest.java @@ -1,15 +1,16 @@ package net.pengcook.recipe.dto; +import jakarta.validation.constraints.NotBlank; import java.util.List; import net.pengcook.ingredient.dto.IngredientCreateRequest; public record RecipeRequest( - String title, - String cookingTime, - String thumbnail, - int difficulty, - String description, - List categories, - List ingredients + @NotBlank String title, + @NotBlank String cookingTime, + @NotBlank String thumbnail, + @NotBlank int difficulty, + @NotBlank String description, + @NotBlank List categories, + @NotBlank List ingredients ) { } diff --git a/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java b/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java index 5e5dd959..d36ef648 100644 --- a/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java +++ b/backend/src/main/java/net/pengcook/recipe/dto/RecipeStepRequest.java @@ -1,4 +1,12 @@ package net.pengcook.recipe.dto; -public record RecipeStepRequest(String image, String description, int sequence, String cookingTime) { +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; + +public record RecipeStepRequest( + String image, + @NotBlank String description, + @Positive int sequence, + @NotBlank String cookingTime +) { } From 76b268df743b45c992e4b10d49c57698fca58a9d Mon Sep 17 00:00:00 2001 From: hyun Date: Sat, 3 Aug 2024 11:52:32 +0900 Subject: [PATCH 15/16] :art: apply code conventions --- .../recipe/controller/RecipeController.java | 18 ++++++++------ .../repository/RecipeStepRepository.java | 2 +- .../recipe/service/RecipeService.java | 24 +++++++++++++------ .../controller/RecipeControllerTest.java | 4 ++-- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java index 4a82d273..02c2b55f 100644 --- a/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java +++ b/backend/src/main/java/net/pengcook/recipe/controller/RecipeController.java @@ -40,9 +40,9 @@ public RecipeResponse createRecipe(@LoginUser UserInfo userInfo, @RequestBody Re return recipeService.createRecipe(userInfo, recipeRequest); } - @GetMapping("/{id}/steps") - public List readRecipeSteps(@PathVariable long id) { - return recipeService.readRecipeSteps(id); + @GetMapping("/{recipeId}/steps") + public List readRecipeSteps(@PathVariable long recipeId) { + return recipeService.readRecipeSteps(recipeId); } @GetMapping("/{recipeId}/steps/{sequence}") @@ -52,13 +52,17 @@ public RecipeStepResponse readRecipeStep(@PathVariable long recipeId, @PathVaria @PostMapping("/{recipeId}/steps") @ResponseStatus(HttpStatus.CREATED) - public RecipeStepResponse createRecipeStep(@PathVariable long recipeId, - @RequestBody RecipeStepRequest recipeStepRequest) { + public RecipeStepResponse createRecipeStep( + @PathVariable long recipeId, + @RequestBody RecipeStepRequest recipeStepRequest + ) { return recipeService.createRecipeStep(recipeId, recipeStepRequest); } @GetMapping("/search") - public List readRecipesOfCategory(@ModelAttribute RecipeOfCategoryRequest request) { - return recipeService.readRecipesOfCategory(request); + public List readRecipesOfCategory( + @ModelAttribute RecipeOfCategoryRequest recipeOfCategoryRequest + ) { + return recipeService.readRecipesOfCategory(recipeOfCategoryRequest); } } diff --git a/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java b/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java index 550d56a6..3c33c18d 100644 --- a/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java +++ b/backend/src/main/java/net/pengcook/recipe/repository/RecipeStepRepository.java @@ -7,7 +7,7 @@ public interface RecipeStepRepository extends JpaRepository { - List findAllByRecipeIdOrderBySequence(long id); + List findAllByRecipeIdOrderBySequence(long recipeId); Optional findByRecipeIdAndSequence(long recipeId, long sequence); } diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index 6a87d6de..8b736f44 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -55,8 +55,14 @@ public List readRecipes(int pageNumber, int pageSize) { public RecipeResponse createRecipe(UserInfo userInfo, RecipeRequest recipeRequest) { User author = userRepository.findById(userInfo.getId()).orElseThrow(); - Recipe recipe = new Recipe(recipeRequest.title(), author, LocalTime.parse(recipeRequest.cookingTime()), - recipeRequest.thumbnail(), recipeRequest.difficulty(), recipeRequest.description()); + Recipe recipe = new Recipe( + recipeRequest.title(), + author, + LocalTime.parse(recipeRequest.cookingTime()), + recipeRequest.thumbnail(), + recipeRequest.difficulty(), + recipeRequest.description() + ); Recipe savedRecipe = recipeRepository.save(recipe); categoryService.saveCategories(savedRecipe, recipeRequest.categories()); @@ -65,8 +71,8 @@ public RecipeResponse createRecipe(UserInfo userInfo, RecipeRequest recipeReques return new RecipeResponse(savedRecipe); } - public List readRecipeSteps(long id) { - List recipeSteps = recipeStepRepository.findAllByRecipeIdOrderBySequence(id); + public List readRecipeSteps(long recipeId) { + List recipeSteps = recipeStepRepository.findAllByRecipeIdOrderBySequence(recipeId); return convertToRecipeStepResponses(recipeSteps); } @@ -81,11 +87,15 @@ public RecipeStepResponse createRecipeStep(long recipeId, RecipeStepRequest reci Recipe recipe = recipeRepository.findById(recipeId) .orElseThrow(() -> new NotFoundException("해당되는 레시피가 없습니다.")); - RecipeStep recipeStep = new RecipeStep(recipe, recipeStepRequest.image(), recipeStepRequest.description(), - recipeStepRequest.sequence(), LocalTime.parse(recipeStepRequest.cookingTime())); + RecipeStep recipeStep = new RecipeStep( + recipe, + recipeStepRequest.image(), + recipeStepRequest.description(), + recipeStepRequest.sequence(), + LocalTime.parse(recipeStepRequest.cookingTime()) + ); RecipeStep savedRecipeStep = recipeStepRepository.save(recipeStep); - return new RecipeStepResponse(savedRecipeStep); } diff --git a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java index 8fdd7969..7e61c14d 100644 --- a/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java +++ b/backend/src/test/java/net/pengcook/recipe/controller/RecipeControllerTest.java @@ -121,7 +121,7 @@ void readRecipeSteps() { "특정 레시피의 상세 스텝을 조회합니다.", "레시피 상세 스텝 조회 API", pathParameters( - parameterWithName("id").description("레시피 아이디") + parameterWithName("recipeId").description("레시피 아이디") ), responseFields( fieldWithPath("[]").description("레시피 스텝 목록"), @@ -132,7 +132,7 @@ void readRecipeSteps() { fieldWithPath("[].sequence").description("레시피 스텝 순서") ))) .when() - .get("/api/recipes/{id}/steps", 1L) + .get("/api/recipes/{recipeId}/steps", 1L) .then().log().all() .body("size()", is(3)); } From f3145f319ab52fcab9fdc41053444c58d9ee1205 Mon Sep 17 00:00:00 2001 From: hyun Date: Sat, 3 Aug 2024 15:14:53 +0900 Subject: [PATCH 16/16] :recycle: generate image URL --- .../net/pengcook/recipe/service/RecipeService.java | 9 ++++++--- .../pengcook/recipe/service/RecipeServiceTest.java | 11 ++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java index 8b736f44..2c092a84 100644 --- a/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java +++ b/backend/src/main/java/net/pengcook/recipe/service/RecipeService.java @@ -10,6 +10,7 @@ import net.pengcook.category.dto.RecipeOfCategoryRequest; import net.pengcook.category.repository.CategoryRecipeRepository; import net.pengcook.category.service.CategoryService; +import net.pengcook.image.service.S3ClientService; import net.pengcook.ingredient.service.IngredientService; import net.pengcook.recipe.domain.Recipe; import net.pengcook.recipe.domain.RecipeStep; @@ -44,6 +45,7 @@ public class RecipeService { private final CategoryService categoryService; private final IngredientService ingredientService; + private final S3ClientService s3ClientService; public List readRecipes(int pageNumber, int pageSize) { Pageable pageable = PageRequest.of(pageNumber, pageSize); @@ -55,11 +57,12 @@ public List readRecipes(int pageNumber, int pageSize) { public RecipeResponse createRecipe(UserInfo userInfo, RecipeRequest recipeRequest) { User author = userRepository.findById(userInfo.getId()).orElseThrow(); + String thumbnailUrl = s3ClientService.getImageUrl(recipeRequest.thumbnail()).url(); Recipe recipe = new Recipe( recipeRequest.title(), author, LocalTime.parse(recipeRequest.cookingTime()), - recipeRequest.thumbnail(), + thumbnailUrl, recipeRequest.difficulty(), recipeRequest.description() ); @@ -86,10 +89,10 @@ public RecipeStepResponse readRecipeStep(long recipeId, long sequence) { public RecipeStepResponse createRecipeStep(long recipeId, RecipeStepRequest recipeStepRequest) { Recipe recipe = recipeRepository.findById(recipeId) .orElseThrow(() -> new NotFoundException("해당되는 레시피가 없습니다.")); - + String imageUrl = s3ClientService.getImageUrl(recipeStepRequest.image()).url(); RecipeStep recipeStep = new RecipeStep( recipe, - recipeStepRequest.image(), + imageUrl, recipeStepRequest.description(), recipeStepRequest.sequence(), LocalTime.parse(recipeStepRequest.cookingTime()) diff --git a/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java b/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java index 438e3b4b..77e22313 100644 --- a/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java +++ b/backend/src/test/java/net/pengcook/recipe/service/RecipeServiceTest.java @@ -8,12 +8,8 @@ import java.util.stream.Stream; import net.pengcook.authentication.domain.UserInfo; import net.pengcook.category.dto.RecipeOfCategoryRequest; -import net.pengcook.category.service.CategoryService; import net.pengcook.ingredient.domain.Requirement; import net.pengcook.ingredient.dto.IngredientCreateRequest; -import net.pengcook.ingredient.service.IngredientRecipeService; -import net.pengcook.ingredient.service.IngredientService; -import net.pengcook.ingredient.service.IngredientSubstitutionService; import net.pengcook.recipe.dto.MainRecipeResponse; import net.pengcook.recipe.dto.RecipeRequest; import net.pengcook.recipe.dto.RecipeResponse; @@ -26,13 +22,10 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.context.annotation.Import; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; -@DataJpaTest -@Import({RecipeService.class, CategoryService.class, IngredientService.class, IngredientRecipeService.class, - IngredientSubstitutionService.class}) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Sql(value = "/data/recipe.sql") class RecipeServiceTest {