From 74b6f25e24ba98eb9be270543a6ac3cfba816d91 Mon Sep 17 00:00:00 2001 From: Natalia Cholewa Date: Fri, 9 Jan 2026 14:54:42 +0100 Subject: [PATCH] feat: language in recipes --- .../client/utils/DefaultValueFactory.java | 1 + commons/src/main/java/commons/Recipe.java | 23 +++++++++++++-- .../java/server/api/RecipeController.java | 28 +++++++++++++++---- .../server/database/RecipeRepository.java | 9 ++++++ .../java/server/service/RecipeService.java | 9 ++++++ .../java/server/api/RecipeControllerTest.java | 28 +++++++++++++++++-- 6 files changed, 86 insertions(+), 12 deletions(-) diff --git a/client/src/main/java/client/utils/DefaultValueFactory.java b/client/src/main/java/client/utils/DefaultValueFactory.java index 7d7f52d..299ae53 100644 --- a/client/src/main/java/client/utils/DefaultValueFactory.java +++ b/client/src/main/java/client/utils/DefaultValueFactory.java @@ -26,6 +26,7 @@ public class DefaultValueFactory { return new Recipe( null, "Untitled recipe", + "en", List.of(), List.of()); } diff --git a/commons/src/main/java/commons/Recipe.java b/commons/src/main/java/commons/Recipe.java index 987edef..b65d76c 100644 --- a/commons/src/main/java/commons/Recipe.java +++ b/commons/src/main/java/commons/Recipe.java @@ -49,6 +49,10 @@ public class Recipe { @Column(name = "name", nullable = false, unique = true) private String name; + // Locale in which the recipe was created. + @Column(name = "locale", nullable = false) + private String locale = "en"; + // Creates another table named recipe_ingredients which stores: // recipe_ingredients(recipe_id -> recipes(id), ingredient). // Example recipe_ingredients table: @@ -95,11 +99,15 @@ public class Recipe { this.name = name; } - // TODO: Replace String with Embeddable Ingredient Class for ingredients - public Recipe(Long id, String name, List ingredients, List preparationSteps) { + public Recipe(Long id, + String name, + String locale, + List ingredients, + List preparationSteps) { // Not used by JPA/Spring this.id = id; this.name = name; + this.locale = locale; this.ingredients = ingredients; this.preparationSteps = preparationSteps; } @@ -120,6 +128,14 @@ public class Recipe { this.name = name; } + public String getLocale() { + return locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + // TODO: Replace String with Embeddable Ingredient Class public List getIngredients() { // Disallow modifying the returned list. @@ -159,7 +175,7 @@ public class Recipe { @Override public String toString() { return "Recipe " + id + - " - " + name + + " - " + name + " (" + locale + ")" + ": " + ingredients.size() + " ingredients / " + preparationSteps.size() + " steps"; } @@ -170,6 +186,7 @@ public class Recipe { return "Recipe{" + "id=" + id + ", name='" + name + '\'' + + ", locale='" + locale + "'" + ", ingredients=" + ingredients + ", preparationSteps=" + preparationSteps + '}'; diff --git a/server/src/main/java/server/api/RecipeController.java b/server/src/main/java/server/api/RecipeController.java index e2143e7..680da4f 100644 --- a/server/src/main/java/server/api/RecipeController.java +++ b/server/src/main/java/server/api/RecipeController.java @@ -56,19 +56,35 @@ public class RecipeController { } /** - * Mapping for GET /recipes(?limit=) + * Mapping for GET /recipes(?limit=)(&locales=) *

* If the limit parameter is unspecified, return all recipes in the repository. * @param limit Integer limit of items you want to get * @return The list of recipes */ @GetMapping("/recipes") - public ResponseEntity> getRecipes(@RequestParam Optional limit) { + public ResponseEntity> getRecipes( + @RequestParam Optional> locales, + @RequestParam Optional limit + ) { logger.info("GET /recipes called."); - return ResponseEntity.ok( - // Choose the right overload. One has a limit, other doesn't. - limit.map(recipeService::findAll).orElseGet(recipeService::findAll) - ); + + // TODO: maybe refactor this. this is horrid and evil and nightmare + var recipes = locales + .map(loc -> { + return limit.map(lim -> { + return recipeService.findAllWithLocales(loc, lim); + }) + .orElseGet(() -> { + return recipeService.findAllWithLocales(loc); + }); + }) + .orElseGet( + // Choose the right overload. One has a limit, other doesn't. + () -> limit.map(recipeService::findAll).orElseGet(recipeService::findAll)); + + + return ResponseEntity.ok(recipes); } /** diff --git a/server/src/main/java/server/database/RecipeRepository.java b/server/src/main/java/server/database/RecipeRepository.java index 8657873..f350842 100644 --- a/server/src/main/java/server/database/RecipeRepository.java +++ b/server/src/main/java/server/database/RecipeRepository.java @@ -19,6 +19,15 @@ import org.springframework.data.jpa.repository.JpaRepository; import commons.Recipe; +import java.util.Collection; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + public interface RecipeRepository extends JpaRepository { boolean existsByName(String name); + + List findAllByLocaleIsIn(Collection locales); + Page findAllByLocaleIsIn(Collection locales, Pageable pageable); } \ No newline at end of file diff --git a/server/src/main/java/server/service/RecipeService.java b/server/src/main/java/server/service/RecipeService.java index ac38e3c..14e819a 100644 --- a/server/src/main/java/server/service/RecipeService.java +++ b/server/src/main/java/server/service/RecipeService.java @@ -7,6 +7,7 @@ import server.database.IngredientRepository; import server.database.RecipeIngredientRepository; import server.database.RecipeRepository; +import java.util.Collection; import java.util.List; import java.util.Optional; @@ -36,6 +37,14 @@ public class RecipeService { return recipeRepository.findAll(PageRequest.of(0, limit)).toList(); } + public List findAllWithLocales(Collection locales) { + return recipeRepository.findAllByLocaleIsIn(locales); + } + + public List findAllWithLocales(Collection locales, int limit) { + return recipeRepository.findAllByLocaleIsIn(locales, PageRequest.of(0, limit)).toList(); + } + /** * Creates a new recipe. Returns empty if the recipe with the same name already exists. * @param recipe Recipe to be saved in the db. diff --git a/server/src/test/java/server/api/RecipeControllerTest.java b/server/src/test/java/server/api/RecipeControllerTest.java index c052688..bede814 100644 --- a/server/src/test/java/server/api/RecipeControllerTest.java +++ b/server/src/test/java/server/api/RecipeControllerTest.java @@ -64,7 +64,7 @@ public class RecipeControllerTest { public void setup(TestInfo info) { recipes = LongStream .range(0, NUM_RECIPES) - .mapToObj(x -> new Recipe(null, "Recipe " + x, List.of(), List.of())) + .mapToObj(x -> new Recipe(null, "Recipe " + x, "en", List.of(), List.of())) .toList(); controller = new RecipeController( recipeService, @@ -107,7 +107,7 @@ public class RecipeControllerTest { @Tag("test-from-init-data") public void getManyRecipes() { // The number of recipes returned is the same as the entire input list - assertEquals(recipes.size(), controller.getRecipes(Optional.empty()).getBody().size()); + assertEquals(recipes.size(), controller.getRecipes(Optional.empty(), Optional.empty()).getBody().size()); } @Test @@ -115,7 +115,29 @@ public class RecipeControllerTest { public void getSomeRecipes() { final int LIMIT = 5; // The number of recipes returned is the same as the entire input list - assertEquals(LIMIT, controller.getRecipes(Optional.of(LIMIT)).getBody().size()); + assertEquals(LIMIT, controller.getRecipes(Optional.empty(), Optional.of(LIMIT)).getBody().size()); + } + + @Test + @Tag("test-from-init-data") + public void getManyRecipesWithLocale() { + // The number of recipes returned is the same as the entire input list + assertEquals(recipes.size(), controller.getRecipes(Optional.of(List.of("en", "nl")), Optional.empty()).getBody().size()); + } + + @Test + @Tag("test-from-init-data") + public void getNoRecipesWithLocale() { + // should have NO Dutch recipes (thank god) + assertEquals(0, controller.getRecipes(Optional.of(List.of("nl")), Optional.empty()).getBody().size()); + } + + @Test + @Tag("test-from-init-data") + public void getSomeRecipesWithLocale() { + final int LIMIT = 5; + // The number of recipes returned is the same as the entire input list + assertEquals(LIMIT, controller.getRecipes(Optional.of(List.of("en", "nl")), Optional.of(LIMIT)).getBody().size()); } @Test