Merge branch 'refactor/server-refactor' into 'main'
refactor/server Closes #44 See merge request cse1105/2025-2026/teams/csep-team-76!41
This commit is contained in:
commit
6010208e33
10 changed files with 249 additions and 130 deletions
|
|
@ -72,6 +72,26 @@ public class Ingredient {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getProteinPer100g() {
|
||||||
|
return proteinPer100g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFatPer100g() {
|
||||||
|
return fatPer100g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCarbsPer100g() {
|
||||||
|
return carbsPer100g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
package server;
|
package server.api;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@Controller
|
@RestController
|
||||||
@RequestMapping("/")
|
@RequestMapping("/")
|
||||||
public class SomeController {
|
public class HealthCheckController {
|
||||||
|
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public String index() {
|
public String index() {
|
||||||
return "Hello world!";
|
return "Server is online!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,8 +5,6 @@ import commons.ws.Topics;
|
||||||
import commons.ws.messages.CreateIngredientMessage;
|
import commons.ws.messages.CreateIngredientMessage;
|
||||||
import commons.ws.messages.DeleteIngredientMessage;
|
import commons.ws.messages.DeleteIngredientMessage;
|
||||||
import commons.ws.messages.UpdateIngredientMessage;
|
import commons.ws.messages.UpdateIngredientMessage;
|
||||||
import org.springframework.data.domain.Example;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
|
@ -19,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import server.database.IngredientRepository;
|
import server.database.IngredientRepository;
|
||||||
|
import server.service.IngredientService;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -42,12 +41,12 @@ import java.util.List;
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class IngredientController {
|
public class IngredientController {
|
||||||
private final IngredientRepository ingredientRepository;
|
private final IngredientService ingredientService;
|
||||||
private final SimpMessagingTemplate messagingTemplate;
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
|
|
||||||
public IngredientController(IngredientRepository ingredientRepository,
|
public IngredientController(IngredientService ingredientService,
|
||||||
SimpMessagingTemplate messagingTemplate) {
|
SimpMessagingTemplate messagingTemplate) {
|
||||||
this.ingredientRepository = ingredientRepository;
|
this.ingredientService = ingredientService;
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,15 +74,9 @@ public class IngredientController {
|
||||||
@RequestParam Optional<Integer> page,
|
@RequestParam Optional<Integer> page,
|
||||||
@RequestParam Optional<Integer> limit
|
@RequestParam Optional<Integer> limit
|
||||||
) {
|
) {
|
||||||
List<Ingredient> ingredients = limit
|
return limit
|
||||||
.map(l -> {
|
.map(integer -> ResponseEntity.ok(ingredientService.findAll(page.orElse(0), integer)))
|
||||||
return ingredientRepository.findAllByOrderByNameAsc(
|
.orElseGet(() -> ResponseEntity.ok(ingredientService.findAll()));
|
||||||
PageRequest.of(page.orElse(0), l)
|
|
||||||
).toList();
|
|
||||||
})
|
|
||||||
.orElseGet(ingredientRepository::findAllByOrderByNameAsc);
|
|
||||||
|
|
||||||
return ResponseEntity.ok(ingredients);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,7 +95,7 @@ public class IngredientController {
|
||||||
*/
|
*/
|
||||||
@GetMapping("/ingredients/{id}")
|
@GetMapping("/ingredients/{id}")
|
||||||
public ResponseEntity<Ingredient> getIngredientById(@PathVariable Long id) {
|
public ResponseEntity<Ingredient> getIngredientById(@PathVariable Long id) {
|
||||||
return ingredientRepository.findById(id)
|
return ingredientService.findById(id)
|
||||||
.map(ResponseEntity::ok)
|
.map(ResponseEntity::ok)
|
||||||
.orElseGet(() -> ResponseEntity.notFound().build());
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
}
|
}
|
||||||
|
|
@ -130,15 +123,13 @@ public class IngredientController {
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody Ingredient updated
|
@RequestBody Ingredient updated
|
||||||
) {
|
) {
|
||||||
if (!ingredientRepository.existsById(id)) {
|
|
||||||
return ResponseEntity.notFound().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
updated.setId(id);
|
updated.setId(id);
|
||||||
Ingredient savedIngredient = ingredientRepository.save(updated);
|
return ingredientService.update(id, updated)
|
||||||
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new CreateIngredientMessage(savedIngredient));
|
.map(saved -> {
|
||||||
|
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new CreateIngredientMessage(saved));
|
||||||
return ResponseEntity.ok(savedIngredient);
|
return ResponseEntity.ok(saved);
|
||||||
|
})
|
||||||
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -164,17 +155,12 @@ public class IngredientController {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ingredient example = new Ingredient();
|
return ingredientService.create(ingredient)
|
||||||
example.name = ingredient.name;
|
.map(saved -> {
|
||||||
|
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new UpdateIngredientMessage(saved));
|
||||||
if (ingredientRepository.existsById(ingredient.id) || ingredientRepository.exists(Example.of(example))) {
|
return ResponseEntity.ok(saved);
|
||||||
return ResponseEntity.badRequest().build();
|
})
|
||||||
}
|
.orElseGet(() -> ResponseEntity.badRequest().build());
|
||||||
|
|
||||||
Ingredient saved = ingredientRepository.save(ingredient);
|
|
||||||
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new UpdateIngredientMessage(saved));
|
|
||||||
|
|
||||||
return ResponseEntity.ok(saved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -193,13 +179,11 @@ public class IngredientController {
|
||||||
*/
|
*/
|
||||||
@DeleteMapping("/ingredients/{id}")
|
@DeleteMapping("/ingredients/{id}")
|
||||||
public ResponseEntity<Boolean> deleteIngredient(@PathVariable Long id) {
|
public ResponseEntity<Boolean> deleteIngredient(@PathVariable Long id) {
|
||||||
if (!ingredientRepository.existsById(id)) {
|
if (!ingredientService.delete(id)) {
|
||||||
return ResponseEntity.notFound().build();
|
return ResponseEntity.notFound().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
ingredientRepository.deleteById(id);
|
|
||||||
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new DeleteIngredientMessage(id));
|
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new DeleteIngredientMessage(id));
|
||||||
|
|
||||||
return ResponseEntity.ok(true);
|
return ResponseEntity.ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ import commons.ws.Topics;
|
||||||
import commons.ws.messages.CreateRecipeMessage;
|
import commons.ws.messages.CreateRecipeMessage;
|
||||||
import commons.ws.messages.DeleteRecipeMessage;
|
import commons.ws.messages.DeleteRecipeMessage;
|
||||||
import commons.ws.messages.UpdateRecipeMessage;
|
import commons.ws.messages.UpdateRecipeMessage;
|
||||||
import org.springframework.data.domain.Example;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
|
|
@ -22,9 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import server.database.IngredientRepository;
|
import server.service.RecipeService;
|
||||||
import server.database.RecipeIngredientRepository;
|
|
||||||
import server.database.RecipeRepository;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -32,19 +28,12 @@ import java.util.Optional;
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api")
|
@RequestMapping("/api")
|
||||||
public class RecipeController {
|
public class RecipeController {
|
||||||
private final RecipeRepository recipeRepository; // JPA repository used in this controller
|
|
||||||
private final SimpMessagingTemplate messagingTemplate;
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
private final RecipeIngredientRepository recipeIngredientRepository;
|
private final RecipeService recipeService;
|
||||||
private final IngredientRepository ingredientRepository;
|
|
||||||
|
|
||||||
public RecipeController(RecipeRepository recipeRepository,
|
public RecipeController(RecipeService recipeService, SimpMessagingTemplate messagingTemplate) {
|
||||||
SimpMessagingTemplate messagingTemplate,
|
this.recipeService = recipeService;
|
||||||
IngredientRepository ingredientRepository,
|
|
||||||
RecipeIngredientRepository recipeIngredientRepository) {
|
|
||||||
this.recipeRepository = recipeRepository;
|
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
this.recipeIngredientRepository = recipeIngredientRepository;
|
|
||||||
this.ingredientRepository = ingredientRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,10 +46,9 @@ public class RecipeController {
|
||||||
*/
|
*/
|
||||||
@GetMapping("/recipe/{id}")
|
@GetMapping("/recipe/{id}")
|
||||||
public ResponseEntity<Recipe> getRecipe(@PathVariable Long id) {
|
public ResponseEntity<Recipe> getRecipe(@PathVariable Long id) {
|
||||||
if (!recipeRepository.existsById(id)) {
|
return recipeService.findById(id)
|
||||||
return ResponseEntity.notFound().build();
|
.map(ResponseEntity::ok)
|
||||||
}
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
return ResponseEntity.ok(recipeRepository.findById(id).get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,28 +60,12 @@ public class RecipeController {
|
||||||
*/
|
*/
|
||||||
@GetMapping("/recipes")
|
@GetMapping("/recipes")
|
||||||
public ResponseEntity<List<Recipe>> getRecipes(@RequestParam Optional<Integer> limit) {
|
public ResponseEntity<List<Recipe>> getRecipes(@RequestParam Optional<Integer> limit) {
|
||||||
if (limit.isPresent()) {
|
return ResponseEntity.ok(
|
||||||
return ResponseEntity.ok(
|
// Choose the right overload. One has a limit, other doesn't.
|
||||||
recipeRepository.findAll(
|
limit.map(recipeService::findAll).orElseGet(recipeService::findAll)
|
||||||
PageRequest.of(0, limit.get())
|
);
|
||||||
).toList());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseEntity.ok(recipeRepository.findAll());
|
|
||||||
}
|
|
||||||
private Recipe saveRecipeAndDependencies(Recipe recipe) {
|
|
||||||
recipe.getIngredients()
|
|
||||||
.forEach(recipeIngredient ->
|
|
||||||
recipeIngredient.setIngredient(
|
|
||||||
ingredientRepository
|
|
||||||
.findByName(recipeIngredient.getIngredient().name)
|
|
||||||
.orElseGet(() -> ingredientRepository.save(recipeIngredient.getIngredient())
|
|
||||||
))
|
|
||||||
);
|
|
||||||
recipeIngredientRepository.saveAll(recipe.getIngredients());
|
|
||||||
Recipe saved = recipeRepository.save(recipe);
|
|
||||||
return saved;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Mapping for <code>POST /recipe/{id}</code>.
|
* Mapping for <code>POST /recipe/{id}</code>.
|
||||||
* Also creates the ingredient elements if they do not exist.
|
* Also creates the ingredient elements if they do not exist.
|
||||||
|
|
@ -104,13 +76,12 @@ public class RecipeController {
|
||||||
*/
|
*/
|
||||||
@PostMapping("/recipe/{id}")
|
@PostMapping("/recipe/{id}")
|
||||||
public ResponseEntity<Recipe> updateRecipe(@PathVariable Long id, @RequestBody Recipe recipe) {
|
public ResponseEntity<Recipe> updateRecipe(@PathVariable Long id, @RequestBody Recipe recipe) {
|
||||||
if (!recipeRepository.existsById(id)) {
|
return recipeService.update(id, recipe)
|
||||||
return ResponseEntity.badRequest().build();
|
.map(saved -> {
|
||||||
}
|
messagingTemplate.convertAndSend(Topics.RECIPES, new UpdateRecipeMessage(saved)); // Send to WS.
|
||||||
Recipe saved = saveRecipeAndDependencies(recipe);
|
return ResponseEntity.ok(saved);
|
||||||
messagingTemplate.convertAndSend(Topics.RECIPES, new UpdateRecipeMessage(saved));
|
})
|
||||||
|
.orElseGet(() -> ResponseEntity.notFound().build()); // Recipe with that id not found.
|
||||||
return ResponseEntity.ok(saved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -125,22 +96,12 @@ public class RecipeController {
|
||||||
*/
|
*/
|
||||||
@PutMapping("/recipe/new")
|
@PutMapping("/recipe/new")
|
||||||
public ResponseEntity<Recipe> createRecipe(@RequestBody Recipe recipe) {
|
public ResponseEntity<Recipe> createRecipe(@RequestBody Recipe recipe) {
|
||||||
|
return recipeService.create(recipe)
|
||||||
// We initialize a new example recipe with the name of input recipe
|
.map(saved -> {
|
||||||
// This is the only attribute we are concerned about making sure it's unique
|
messagingTemplate.convertAndSend(Topics.RECIPES, new CreateRecipeMessage(saved)); // Send to WS.
|
||||||
Recipe example = new Recipe();
|
return ResponseEntity.ok(saved);
|
||||||
example.setName(recipe.getName());
|
})
|
||||||
|
.orElseGet(() -> ResponseEntity.badRequest().build()); // That recipe already exists.
|
||||||
/* Here we use very funny JPA magic repository.exists(Example<Recipe>)
|
|
||||||
We check if any recipe in the repository has the same name as the input
|
|
||||||
*/
|
|
||||||
if (recipeRepository.exists(Example.of(example))) {
|
|
||||||
return ResponseEntity.badRequest().build();
|
|
||||||
}
|
|
||||||
Recipe saved = saveRecipeAndDependencies(recipe);
|
|
||||||
messagingTemplate.convertAndSend(Topics.RECIPES, new CreateRecipeMessage(saved));
|
|
||||||
|
|
||||||
return ResponseEntity.ok(saved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -153,13 +114,10 @@ public class RecipeController {
|
||||||
*/
|
*/
|
||||||
@DeleteMapping("/recipe/{id}")
|
@DeleteMapping("/recipe/{id}")
|
||||||
public ResponseEntity<Boolean> deleteRecipe(@PathVariable Long id) {
|
public ResponseEntity<Boolean> deleteRecipe(@PathVariable Long id) {
|
||||||
if (!recipeRepository.existsById(id)) {
|
if (!recipeService.delete(id)) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
recipeRepository.deleteById(id);
|
messagingTemplate.convertAndSend(Topics.RECIPES, new DeleteRecipeMessage(id)); // Send to WS.
|
||||||
|
|
||||||
messagingTemplate.convertAndSend(Topics.RECIPES, new DeleteRecipeMessage(id));
|
|
||||||
|
|
||||||
return ResponseEntity.ok(true);
|
return ResponseEntity.ok(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,7 @@ public interface IngredientRepository extends JpaRepository<Ingredient, Long> {
|
||||||
List<Ingredient> findAllByOrderByNameAsc();
|
List<Ingredient> findAllByOrderByNameAsc();
|
||||||
Page<Ingredient> findAllByOrderByNameAsc(Pageable pageable);
|
Page<Ingredient> findAllByOrderByNameAsc(Pageable pageable);
|
||||||
Optional<Ingredient> findByName(String name);
|
Optional<Ingredient> findByName(String name);
|
||||||
|
|
||||||
|
boolean existsByName(String name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,6 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import commons.Recipe;
|
import commons.Recipe;
|
||||||
|
|
||||||
public interface RecipeRepository extends JpaRepository<Recipe, Long> {}
|
public interface RecipeRepository extends JpaRepository<Recipe, Long> {
|
||||||
|
boolean existsByName(String name);
|
||||||
|
}
|
||||||
67
server/src/main/java/server/service/IngredientService.java
Normal file
67
server/src/main/java/server/service/IngredientService.java
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
package server.service;
|
||||||
|
|
||||||
|
import commons.Ingredient;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import server.database.IngredientRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class IngredientService {
|
||||||
|
IngredientRepository ingredientRepository;
|
||||||
|
|
||||||
|
public IngredientService(IngredientRepository ingredientRepository) {
|
||||||
|
this.ingredientRepository = ingredientRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Ingredient> findById(Long id) {
|
||||||
|
return ingredientRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Ingredient> findAll() {
|
||||||
|
return ingredientRepository.findAllByOrderByNameAsc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Ingredient> findAll(int page, int limit) {
|
||||||
|
return ingredientRepository.findAllByOrderByNameAsc(PageRequest.of(page, limit)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ingredient. Returns empty if the recipe with the same name or id already exists.
|
||||||
|
* @param ingredient Ingredient to be saved in the db.
|
||||||
|
* @return The created ingredient (the ingredient arg with a new assigned id) or empty if it already exists in db.
|
||||||
|
*/
|
||||||
|
public Optional<Ingredient> create(Ingredient ingredient) {
|
||||||
|
if (ingredientRepository.existsByName(ingredient.getName()) ||
|
||||||
|
ingredientRepository.existsById(ingredient.getId())) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(ingredientRepository.save(ingredient));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an ingredient. The ingredient with the provided id will be replaced (in db) with the provided ingredient.
|
||||||
|
* @param id id of the ingredient to update.
|
||||||
|
* @param ingredient Ingredient to be saved in the db.
|
||||||
|
* @return The created ingredient (the ingredient arg with a new assigned id.)
|
||||||
|
*/
|
||||||
|
public Optional<Ingredient> update(Long id, Ingredient ingredient) {
|
||||||
|
assert id.equals(ingredient.getId()) :
|
||||||
|
"The id of the updated ingredient doesn't match the provided ingredient's id.";
|
||||||
|
if (!ingredientRepository.existsById(id)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(ingredientRepository.save(ingredient));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean delete(Long id) {
|
||||||
|
if (!ingredientRepository.existsById(id)) return false;
|
||||||
|
ingredientRepository.deleteById(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
89
server/src/main/java/server/service/RecipeService.java
Normal file
89
server/src/main/java/server/service/RecipeService.java
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package server.service;
|
||||||
|
|
||||||
|
import commons.Recipe;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import server.database.IngredientRepository;
|
||||||
|
import server.database.RecipeIngredientRepository;
|
||||||
|
import server.database.RecipeRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RecipeService {
|
||||||
|
RecipeRepository recipeRepository;
|
||||||
|
IngredientRepository ingredientRepository;
|
||||||
|
RecipeIngredientRepository recipeIngredientRepository;
|
||||||
|
|
||||||
|
public RecipeService(RecipeRepository recipeRepository,
|
||||||
|
IngredientRepository ingredientRepository,
|
||||||
|
RecipeIngredientRepository recipeIngredientRepository) {
|
||||||
|
this.recipeRepository = recipeRepository;
|
||||||
|
this.ingredientRepository = ingredientRepository;
|
||||||
|
this.recipeIngredientRepository = recipeIngredientRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Recipe> findById(Long id) {
|
||||||
|
return recipeRepository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Recipe> findAll() {
|
||||||
|
return recipeRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Recipe> findAll(int limit) {
|
||||||
|
return recipeRepository.findAll(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.
|
||||||
|
* @return The created recipe (the recipe arg with a new assigned id) or empty if it already exists in db.
|
||||||
|
*/
|
||||||
|
public Optional<Recipe> create(Recipe recipe) {
|
||||||
|
if (recipeRepository.existsByName(recipe.getName())) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(saveWithDependencies(recipe));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a recipe. The recipe with the provided id will be replaced with the provided recipe.
|
||||||
|
* Automatically updates ingredients and any dependencies of recipe.
|
||||||
|
* @param id id of the recipe to update.
|
||||||
|
* @param recipe Recipe to be saved in the db.
|
||||||
|
* @return The created recipe (the recipe arg with a new assigned id.)
|
||||||
|
*/
|
||||||
|
public Optional<Recipe> update(Long id, Recipe recipe) {
|
||||||
|
assert id.equals(recipe.getId()) : "The id of the updated recipe doesn't match the provided recipes id.";
|
||||||
|
if (!recipeRepository.existsById(id)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(saveWithDependencies(recipe));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean delete(Long id) {
|
||||||
|
// TODO: Propagate deletion to ingredients.
|
||||||
|
if (!recipeRepository.existsById(id)) return false;
|
||||||
|
recipeRepository.deleteById(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Recipe saveWithDependencies(Recipe recipe) {
|
||||||
|
// TODO: try to automate this with JFX somehow.
|
||||||
|
recipe.getIngredients()
|
||||||
|
.forEach(recipeIngredient ->
|
||||||
|
recipeIngredient.setIngredient(
|
||||||
|
ingredientRepository
|
||||||
|
.findByName(recipeIngredient.getIngredient().name)
|
||||||
|
.orElseGet(() -> ingredientRepository.save(recipeIngredient.getIngredient())
|
||||||
|
))
|
||||||
|
);
|
||||||
|
recipeIngredientRepository.saveAll(recipe.getIngredients());
|
||||||
|
return recipeRepository.save(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import server.database.IngredientRepository;
|
import server.database.IngredientRepository;
|
||||||
import server.WebSocketConfig;
|
import server.WebSocketConfig;
|
||||||
|
import server.service.IngredientService;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
|
@ -20,10 +21,11 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
@DataJpaTest
|
@DataJpaTest
|
||||||
@ActiveProfiles("mock-data-test")
|
@ActiveProfiles("mock-data-test")
|
||||||
@Import(WebSocketConfig.class)
|
@Import({WebSocketConfig.class, IngredientService.class})
|
||||||
public class IngredientControllerTest {
|
public class IngredientControllerTest {
|
||||||
private final SimpMessagingTemplate template;
|
private final SimpMessagingTemplate template;
|
||||||
private final IngredientRepository ingredientRepository;
|
private final IngredientRepository ingredientRepository;
|
||||||
|
private final IngredientService ingredientService;
|
||||||
private IngredientController controller;
|
private IngredientController controller;
|
||||||
|
|
||||||
private static final double PROTEIN_BASE = 1.0;
|
private static final double PROTEIN_BASE = 1.0;
|
||||||
|
|
@ -42,8 +44,10 @@ public class IngredientControllerTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public IngredientControllerTest(IngredientRepository ingredientRepository,
|
public IngredientControllerTest(IngredientRepository ingredientRepository,
|
||||||
|
IngredientService ingredientService,
|
||||||
SimpMessagingTemplate template) {
|
SimpMessagingTemplate template) {
|
||||||
this.ingredientRepository = ingredientRepository;
|
this.ingredientRepository = ingredientRepository;
|
||||||
|
this.ingredientService = ingredientService;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +63,7 @@ public class IngredientControllerTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
controller = new IngredientController(ingredientRepository, template);
|
controller = new IngredientController(ingredientService, template);
|
||||||
|
|
||||||
this.createInitialIngredients();
|
this.createInitialIngredients();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,8 @@ import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import server.WebSocketConfig;
|
import server.WebSocketConfig;
|
||||||
import server.database.IngredientRepository;
|
|
||||||
import server.database.RecipeIngredientRepository;
|
|
||||||
import server.database.RecipeRepository;
|
import server.database.RecipeRepository;
|
||||||
|
import server.service.RecipeService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -38,30 +37,27 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
// This is required to enable WebSocket messaging in tests
|
// This is required to enable WebSocket messaging in tests
|
||||||
//
|
//
|
||||||
// Without this line, Spring screams about missing SimpMessagingTemplate bean
|
// Without this line, Spring screams about missing SimpMessagingTemplate bean
|
||||||
@Import(WebSocketConfig.class)
|
@Import({WebSocketConfig.class, RecipeService.class})
|
||||||
public class RecipeControllerTest {
|
public class RecipeControllerTest {
|
||||||
|
private final RecipeService recipeService;
|
||||||
|
private final RecipeRepository recipeRepository;
|
||||||
private final SimpMessagingTemplate template;
|
private final SimpMessagingTemplate template;
|
||||||
private RecipeController controller;
|
private RecipeController controller;
|
||||||
|
|
||||||
private List<Recipe> recipes;
|
private List<Recipe> recipes;
|
||||||
private final RecipeRepository recipeRepository;
|
|
||||||
private List<Long> recipeIds;
|
private List<Long> recipeIds;
|
||||||
public static final int NUM_RECIPES = 10;
|
public static final int NUM_RECIPES = 10;
|
||||||
private final IngredientRepository ingredientRepository;
|
|
||||||
private final RecipeIngredientRepository recipeIngredientRepository;
|
|
||||||
|
|
||||||
// Injects a test repository into the test class
|
// Injects a test repository into the test class
|
||||||
@Autowired
|
@Autowired
|
||||||
public RecipeControllerTest(
|
public RecipeControllerTest(
|
||||||
|
RecipeService recipeService,
|
||||||
RecipeRepository recipeRepository,
|
RecipeRepository recipeRepository,
|
||||||
SimpMessagingTemplate template,
|
SimpMessagingTemplate template
|
||||||
IngredientRepository ingredientRepository,
|
|
||||||
RecipeIngredientRepository recipeIngredientRepository
|
|
||||||
) {
|
) {
|
||||||
|
this.recipeService = recipeService;
|
||||||
this.recipeRepository = recipeRepository;
|
this.recipeRepository = recipeRepository;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.ingredientRepository = ingredientRepository;
|
|
||||||
this.recipeIngredientRepository = recipeIngredientRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
|
@ -71,10 +67,8 @@ public class RecipeControllerTest {
|
||||||
.mapToObj(x -> new Recipe(null, "Recipe " + x, List.of(), List.of()))
|
.mapToObj(x -> new Recipe(null, "Recipe " + x, List.of(), List.of()))
|
||||||
.toList();
|
.toList();
|
||||||
controller = new RecipeController(
|
controller = new RecipeController(
|
||||||
recipeRepository,
|
recipeService,
|
||||||
template,
|
template
|
||||||
ingredientRepository,
|
|
||||||
recipeIngredientRepository
|
|
||||||
);
|
);
|
||||||
Set<String> tags = info.getTags();
|
Set<String> tags = info.getTags();
|
||||||
List<Long> ids = new ArrayList<>();
|
List<Long> ids = new ArrayList<>();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue