From ec6b2260aa806ef99408a96d6e9ef75e1d69e7bf Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 7 Jul 2023 12:13:02 -0600 Subject: [PATCH 01/36] completing assignment --- .../controllers/IndexController.java | 13 ++ .../springframework/domain/Ingredient.java | 59 +++++++++ .../guru/springframework/domain/Notes.java | 41 +++++++ .../guru/springframework/domain/Recipe.java | 116 ++++++++++++++++++ .../springframework/domain/UnitOfMeasure.java | 38 ++++++ 5 files changed, 267 insertions(+) create mode 100644 src/main/java/guru/springframework/controllers/IndexController.java create mode 100644 src/main/java/guru/springframework/domain/Ingredient.java create mode 100644 src/main/java/guru/springframework/domain/Notes.java create mode 100644 src/main/java/guru/springframework/domain/Recipe.java create mode 100644 src/main/java/guru/springframework/domain/UnitOfMeasure.java diff --git a/src/main/java/guru/springframework/controllers/IndexController.java b/src/main/java/guru/springframework/controllers/IndexController.java new file mode 100644 index 0000000000..3b0bf4b219 --- /dev/null +++ b/src/main/java/guru/springframework/controllers/IndexController.java @@ -0,0 +1,13 @@ +package guru.springframework.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class IndexController { + @RequestMapping({"","/","index"}) + public String getIndexPage(){ + System.out.println("Something...changed"); + return "index"; //ligado a una pagina de inicio + } +} diff --git a/src/main/java/guru/springframework/domain/Ingredient.java b/src/main/java/guru/springframework/domain/Ingredient.java new file mode 100644 index 0000000000..5e2c87d6c9 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Ingredient.java @@ -0,0 +1,59 @@ +package guru.springframework.domain; + +import javax.persistence.*; +import java.math.BigDecimal; + +@Entity +public class Ingredient { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String description; + private BigDecimal amount; + //hay que ligar a la clase Receta + @ManyToOne + private Recipe recipe; + //ligamos a la clase unitofmeasure con el comportamiento eager (siempre) + @OneToOne(fetch = FetchType.EAGER) + private UnitOfMeasure unitOfMeasure; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public Recipe getRecipe() { + return recipe; + } + + public void setRecipe(Recipe recipe) { + this.recipe = recipe; + } + + public UnitOfMeasure getUnitOfMeasure() { + return unitOfMeasure; + } + + public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) { + this.unitOfMeasure = unitOfMeasure; + } +} diff --git a/src/main/java/guru/springframework/domain/Notes.java b/src/main/java/guru/springframework/domain/Notes.java new file mode 100644 index 0000000000..cb5269b11a --- /dev/null +++ b/src/main/java/guru/springframework/domain/Notes.java @@ -0,0 +1,41 @@ +package guru.springframework.domain; + +import javax.persistence.*; + +@Entity +public class Notes { + //primary key + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + //relación con Recipe + @OneToOne + private Recipe recipe; + //hay que permitir que se acepte mas que el limite de caracteres (255) con @Lob + @Lob + private String recipeNotes; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Recipe getRecipe() { + return recipe; + } + + public void setRecipe(Recipe recipe) { + this.recipe = recipe; + } + + public String getRecipeNotes() { + return recipeNotes; + } + + public void setRecipeNotes(String recipeNotes) { + this.recipeNotes = recipeNotes; + } +} diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java new file mode 100644 index 0000000000..f9b5fa90f2 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -0,0 +1,116 @@ +package guru.springframework.domain; + +import javax.persistence.*; +import java.util.Set; + +@Entity +public class Recipe { + //primary key generada con la estrategia de identidad + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String description; + private Integer prepTime; + private Integer cookTime; + private Integer servings; + private String source; + private String url; + private String directions; + //private Difficulty difficulty; + @Lob + private Byte[] image; + //propiedad 1 a 1 con cascada lo que significa que el Owning es Recipe + @OneToOne(cascade = CascadeType.ALL) + private Notes notes; + //propiedad 1 a muchos con cascada mapeado por esta clase + @OneToMany(cascade = CascadeType.ALL, mappedBy = "recipe") + private Set ingredients; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getPrepTime() { + return prepTime; + } + + public void setPrepTime(Integer prepTime) { + this.prepTime = prepTime; + } + + public Integer getCookTime() { + return cookTime; + } + + public void setCookTime(Integer cookTime) { + this.cookTime = cookTime; + } + + public Integer getServings() { + return servings; + } + + public void setServings(Integer servings) { + this.servings = servings; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDirections() { + return directions; + } + + public void setDirections(String directions) { + this.directions = directions; + } + + public Byte[] getImage() { + return image; + } + + public void setImage(Byte[] image) { + this.image = image; + } + + public Notes getNotes() { + return notes; + } + + public void setNotes(Notes notes) { + this.notes = notes; + } + + public Set getIngredients() { + return ingredients; + } + + public void setIngredients(Set ingredients) { + this.ingredients = ingredients; + } +} diff --git a/src/main/java/guru/springframework/domain/UnitOfMeasure.java b/src/main/java/guru/springframework/domain/UnitOfMeasure.java new file mode 100644 index 0000000000..06a6de7a08 --- /dev/null +++ b/src/main/java/guru/springframework/domain/UnitOfMeasure.java @@ -0,0 +1,38 @@ +package guru.springframework.domain; + +import javax.persistence.*; + +@Entity +public class UnitOfMeasure { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String uom; + //relación con Ingredient + @OneToOne + private Ingredient ingredient; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUom() { + return uom; + } + + public void setUom(String uom) { + this.uom = uom; + } + + public Ingredient getIngredient() { + return ingredient; + } + + public void setIngredient(Ingredient ingredient) { + this.ingredient = ingredient; + } +} From c336b978b1373127fe5ae032cadc212664896387 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 7 Jul 2023 13:10:33 -0600 Subject: [PATCH 02/36] Adding repos --- .../guru/springframework/domain/Category.java | 16 ++++++++++++ .../springframework/domain/Difficulty.java | 5 ++++ .../guru/springframework/domain/Recipe.java | 25 ++++++++++++++++++- .../repositories/CategoryRepository.java | 8 ++++++ .../repositories/RecipeRepository.java | 7 ++++++ .../repositories/UnitOfMeasureRepository.java | 7 ++++++ src/main/resources/banner.txt | 7 ++++++ src/main/resources/templates/index.html | 10 ++++++++ 8 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/main/java/guru/springframework/domain/Category.java create mode 100644 src/main/java/guru/springframework/domain/Difficulty.java create mode 100644 src/main/java/guru/springframework/repositories/CategoryRepository.java create mode 100644 src/main/java/guru/springframework/repositories/RecipeRepository.java create mode 100644 src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java create mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/templates/index.html diff --git a/src/main/java/guru/springframework/domain/Category.java b/src/main/java/guru/springframework/domain/Category.java new file mode 100644 index 0000000000..2687379502 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Category.java @@ -0,0 +1,16 @@ +package guru.springframework.domain; + +import javax.persistence.*; +import java.util.Set; + +@Entity +public class Category { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String description; + @ManyToMany(mappedBy = "categories") + private Set recipes; + + +} diff --git a/src/main/java/guru/springframework/domain/Difficulty.java b/src/main/java/guru/springframework/domain/Difficulty.java new file mode 100644 index 0000000000..13e606e127 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Difficulty.java @@ -0,0 +1,5 @@ +package guru.springframework.domain; + +public enum Difficulty { + EASY, MODERATE, HARD +} diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index f9b5fa90f2..c93829ea10 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -16,7 +16,9 @@ public class Recipe { private String source; private String url; private String directions; - //private Difficulty difficulty; + //anotacion de enumeracion con valor + @Enumerated(value = EnumType.STRING) + private Difficulty difficulty; @Lob private Byte[] image; //propiedad 1 a 1 con cascada lo que significa que el Owning es Recipe @@ -25,6 +27,11 @@ public class Recipe { //propiedad 1 a muchos con cascada mapeado por esta clase @OneToMany(cascade = CascadeType.ALL, mappedBy = "recipe") private Set ingredients; + @ManyToMany + @JoinTable(name = "recipe_category", + joinColumns = @JoinColumn(name = "recipe_id"), + inverseJoinColumns = @JoinColumn(name = "category_id")) + private Set categories; public Long getId() { return id; @@ -90,6 +97,14 @@ public void setDirections(String directions) { this.directions = directions; } + public Difficulty getDifficulty() { + return difficulty; + } + + public void setDifficulty(Difficulty difficulty) { + this.difficulty = difficulty; + } + public Byte[] getImage() { return image; } @@ -113,4 +128,12 @@ public Set getIngredients() { public void setIngredients(Set ingredients) { this.ingredients = ingredients; } + + public Set getCategories() { + return categories; + } + + public void setCategories(Set categories) { + this.categories = categories; + } } diff --git a/src/main/java/guru/springframework/repositories/CategoryRepository.java b/src/main/java/guru/springframework/repositories/CategoryRepository.java new file mode 100644 index 0000000000..f289bb3978 --- /dev/null +++ b/src/main/java/guru/springframework/repositories/CategoryRepository.java @@ -0,0 +1,8 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.Category; +import org.springframework.data.repository.CrudRepository; + +public interface CategoryRepository extends CrudRepository { + +} diff --git a/src/main/java/guru/springframework/repositories/RecipeRepository.java b/src/main/java/guru/springframework/repositories/RecipeRepository.java new file mode 100644 index 0000000000..6ad1fec334 --- /dev/null +++ b/src/main/java/guru/springframework/repositories/RecipeRepository.java @@ -0,0 +1,7 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.Recipe; +import org.springframework.data.repository.CrudRepository; + +public interface RecipeRepository extends CrudRepository { +} diff --git a/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java b/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java new file mode 100644 index 0000000000..aca01fcdbb --- /dev/null +++ b/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java @@ -0,0 +1,7 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.UnitOfMeasure; +import org.springframework.data.repository.CrudRepository; + +public interface UnitOfMeasureRepository extends CrudRepository { +} diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000000..5ff96800d5 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,7 @@ + ________ _______ ________ ___ ________ _______ ________ ________ ________ +|\ __ \|\ ___ \ |\ ____\|\ \|\ __ \|\ ___ \ |\ __ \|\ __ \|\ __ \ +\ \ \|\ \ \ __/|\ \ \___|\ \ \ \ \|\ \ \ __/| \ \ \|\ \ \ \|\ \ \ \|\ \ + \ \ _ _\ \ \_|/_\ \ \ \ \ \ \ ____\ \ \_|/__ \ \ __ \ \ ____\ \ ____\ + \ \ \\ \\ \ \_|\ \ \ \____\ \ \ \ \___|\ \ \_|\ \ \ \ \ \ \ \ \___|\ \ \___| + \ \__\\ _\\ \_______\ \_______\ \__\ \__\ \ \_______\ \ \__\ \__\ \__\ \ \__\ + \|__|\|__|\|_______|\|_______|\|__|\|__| \|_______| \|__|\|__|\|__| \|__| diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000000..9f865db6bc --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,10 @@ + + + + + Recipe Home + + +

My Recipes! 44

+ + \ No newline at end of file From 860c1cc927c4a00dc563aebff06d7f8d9403e6b0 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 13 Jul 2023 11:22:52 -0600 Subject: [PATCH 03/36] Adding startup page with bootstrap --- .../bootstrap/RecipeBootstrap.java | 151 ++++++++++++++++++ .../controllers/IndexController.java | 19 ++- .../guru/springframework/domain/Category.java | 14 ++ .../springframework/domain/Ingredient.java | 11 ++ .../guru/springframework/domain/Recipe.java | 6 +- .../springframework/domain/UnitOfMeasure.java | 10 +- .../repositories/CategoryRepository.java | 4 + .../repositories/UnitOfMeasureRepository.java | 3 + .../services/RecipeService.java | 10 ++ .../services/RecipeServiceImpl.java | 23 +++ src/main/resources/data.sql | 12 ++ src/main/resources/templates/index.html | 11 ++ 12 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java create mode 100644 src/main/java/guru/springframework/services/RecipeService.java create mode 100644 src/main/java/guru/springframework/services/RecipeServiceImpl.java create mode 100644 src/main/resources/data.sql diff --git a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java new file mode 100644 index 0000000000..071b24f1bc --- /dev/null +++ b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java @@ -0,0 +1,151 @@ +package guru.springframework.bootstrap; + +import guru.springframework.domain.*; +import guru.springframework.repositories.CategoryRepository; +import guru.springframework.repositories.RecipeRepository; +import guru.springframework.repositories.UnitOfMeasureRepository; +import org.aspectj.weaver.ast.Not; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Component +public class RecipeBootstrap implements ApplicationListener { + private final CategoryRepository categoryRepository; + private final RecipeRepository recipeRepository; + private final UnitOfMeasureRepository unitOfMeasureRepository; + + public RecipeBootstrap(CategoryRepository categoryRepository, RecipeRepository recipeRepository, UnitOfMeasureRepository unitOfMeasureRepository) { + this.categoryRepository = categoryRepository; + this.recipeRepository = recipeRepository; + this.unitOfMeasureRepository = unitOfMeasureRepository; + } + //guardar lo que viene de los repositorios + @Override + public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { + recipeRepository.saveAll(getRecipes()); + } + + private List getRecipes(){ + List recipes = new ArrayList<>(2); + + //get Units Of Measure + Optional eachUomOptional = unitOfMeasureRepository.findByDescription("Each"); + if(!eachUomOptional.isPresent()){ + throw new RuntimeException("Expected Each UOM not found"); + } + Optional tableSpoonUomOptional = unitOfMeasureRepository.findByDescription("Tablespoon"); + if(!tableSpoonUomOptional.isPresent()){ + throw new RuntimeException("Expected Tablespoon UOM not found"); + } + Optional teaSpoonUomOptional = unitOfMeasureRepository.findByDescription("Teaspoon"); + if(!teaSpoonUomOptional.isPresent()){ + throw new RuntimeException("Expected Teaspoon UOM not found"); + } + Optional dashUOMOptional = unitOfMeasureRepository.findByDescription("Dash"); + if(!dashUOMOptional.isPresent()){ + throw new RuntimeException("Expected Dash UOM not found"); + } + Optional pintUOMOptional = unitOfMeasureRepository.findByDescription("Pint"); + if(!pintUOMOptional.isPresent()){ + throw new RuntimeException("Expected Pint UOM not found"); + } + Optional cupsUOMOptional = unitOfMeasureRepository.findByDescription("Cup"); + if(!cupsUOMOptional.isPresent()){ + throw new RuntimeException("Expected Cup UOM not found"); + } + + //get optionals + UnitOfMeasure eachUom = eachUomOptional.get(); + UnitOfMeasure tableSpoonUOM = tableSpoonUomOptional.get(); + UnitOfMeasure teaspoonUOM = teaSpoonUomOptional.get(); + UnitOfMeasure dashUom = dashUOMOptional.get(); + UnitOfMeasure pintUom = pintUOMOptional.get(); + UnitOfMeasure cupsUom = cupsUOMOptional.get(); + + //Categorias + Optional americanCategoryOptional = categoryRepository.findByDescription("American"); + if(!americanCategoryOptional.isPresent()){ + throw new RuntimeException("Expected Category not found"); + } + Optional mexicanCategoryOptional = categoryRepository.findByDescription("Mexican"); + if(!mexicanCategoryOptional.isPresent()){ + throw new RuntimeException("Expected Category not found"); + } + + Category americanCategory = americanCategoryOptional.get(); + Category mexicanCategory = mexicanCategoryOptional.get(); + + //Creamos la receta + Recipe guacRecipe = new Recipe(); + guacRecipe.setDescription("Perfect Guacamole"); + guacRecipe.setPrepTime(10); + guacRecipe.setCookTime(0); + guacRecipe.setDifficulty(Difficulty.EASY); + guacRecipe.setDirections("1. Cut the avocados: Cut the avocados in half. Remove the pit. Score the inside of the avocado with a blunt knife and scoop out the flesh with a spoon. Place in a bowl\n" + + "2. Mash the avocado flesh: Using a fork, roughly mash the avocado. \n" + + "3. Add the remaining ingredients to taste: Sprinkle with salt and lime (or lemon) juice. Add the chopped onion, cilantro, black pepper, and chilis.\n" + + "4. Serve immediately\n"); + Notes guacNotes = new Notes(); + guacNotes.setRecipeNotes("Chilling tomatoes hurts their flavor.\n" + + "So, if you want to add chopped tomato to your guacamole, add it just before serving.\n" + + "Read more: https://www.simplyrecipes.com/recipes/perfect_guacamole/"); + guacNotes.setRecipe(guacRecipe); + guacRecipe.setNotes(guacNotes); + guacRecipe.getIngredients().add(new Ingredient("Ripe Avocados",new BigDecimal(2), guacRecipe, eachUom)); + guacRecipe.getIngredients().add(new Ingredient("Kosher salt",new BigDecimal(0.5),guacRecipe, teaspoonUOM)); + guacRecipe.getIngredients().add(new Ingredient("Fresh lime/lemon juice",new BigDecimal(2),guacRecipe, tableSpoonUOM)); + guacRecipe.getIngredients().add(new Ingredient("Minced Red/green onion ",new BigDecimal(2),guacRecipe, tableSpoonUOM)); + guacRecipe.getIngredients().add(new Ingredient("Serrano chiles, stems and seeds removed",new BigDecimal(2),guacRecipe, eachUom)); + guacRecipe.getIngredients().add(new Ingredient("Cilantro",new BigDecimal(2),guacRecipe, tableSpoonUOM)); + guacRecipe.getIngredients().add(new Ingredient("Freshly grated black pepper",new BigDecimal(2),guacRecipe, dashUom)); + guacRecipe.getIngredients().add(new Ingredient("Ripe tomato, seeds and pulp removed",new BigDecimal(0.5),guacRecipe, eachUom)); + + guacRecipe.getCategories().add(americanCategory); + guacRecipe.getCategories().add(mexicanCategory); + + recipes.add(guacRecipe); + + //Tacos + Recipe tacosRecipe = new Recipe(); + tacosRecipe.setDescription("Spicy Grilled Chicken Taco"); + tacosRecipe.setCookTime(15); + tacosRecipe.setPrepTime(20); + tacosRecipe.setDifficulty(Difficulty.MODERATE); + + tacosRecipe.setDirections("1. Prepare the grill: Prepare either a gas or charcoal grill for medium-high, direct heat.\n" + + "2. Make the marinade and coat the chicken: In a large bowl, stir together the chili powder, oregano, cumin, sugar, salt, garlic and orange zest. Stir in the orange juice and olive oil to make a loose paste. Add the chicken to the bowl and toss to coat all over. Set aside.\n" + + "3. Grill the chicken: Grill the chicken for 3 to 4 minutes per side, or until a thermometer inserted into the thickest part of the meat registers 165°F. Transfer to a plate and rest for 5 minutes.\n" + + "4. Thin the sour cream with milk: Stir together the sour cream and milk to thin out the sour cream to make it easy to drizzle.\n" + + "5. Assemble the tacos: Slice the chicken into strips. On each tortilla, place a small handful of arugula. Top with chicken slices, sliced avocado, radishes, tomatoes, and onion slices. Drizzle with the thinned sour cream. Serve with lime wedges.\n" + + "6. Warm the tortillas: Place each tortilla on the grill or on a hot, dry skillet over medium-high heat. As soon as you see pockets of the air start to puff up in the tortilla, turn it with tongs and heat for a few seconds on the other side. Wrap warmed tortillas in a tea towel to keep them warm until serving.\n"); + + Notes tacoNotes = new Notes(); + tacoNotes.setRecipeNotes("Look for ancho chile powder with the Mexican ingredients at your grocery store, on buy it online. (If you can't find ancho chili powder, you replace the ancho chili, the oregano, and the cumin with 2 1/2 tablespoons regular chili powder, though the flavor won't be quite the same.)\n" + + "Read more: https://www.simplyrecipes.com/recipes/spicy_grilled_chicken_tacos/\n"); + tacoNotes.setRecipe(tacosRecipe); + tacosRecipe.setNotes(tacoNotes); + + tacosRecipe.getIngredients().add(new Ingredient("Ancho chilli powder", new BigDecimal(2), tacosRecipe, tableSpoonUOM)); + tacosRecipe.getIngredients().add(new Ingredient("Dried oregano", new BigDecimal(1), tacosRecipe, teaspoonUOM)); + tacosRecipe.getIngredients().add(new Ingredient("Dried Cumin", new BigDecimal(1), tacosRecipe, teaspoonUOM)); + tacosRecipe.getIngredients().add(new Ingredient("Salt", new BigDecimal(0.5), tacosRecipe, teaspoonUOM)); + tacosRecipe.getIngredients().add(new Ingredient("Corn tortillas", new BigDecimal(8), tacosRecipe, eachUom)); + tacosRecipe.getIngredients().add(new Ingredient("Avocado, sliced", new BigDecimal(2), tacosRecipe, eachUom)); + tacosRecipe.getIngredients().add(new Ingredient("Boneless Chicken thighs", new BigDecimal(6), tacosRecipe, eachUom)); + tacosRecipe.getIngredients().add(new Ingredient("Onion, sliced", new BigDecimal(0.25), tacosRecipe, eachUom)); + tacosRecipe.getIngredients().add(new Ingredient("Sour cream", new BigDecimal(0.5), tacosRecipe, cupsUom)); + tacosRecipe.getIngredients().add(new Ingredient("Lime/lemon", new BigDecimal(1), tacosRecipe, eachUom)); + + tacosRecipe.getCategories().add(americanCategory); + tacosRecipe.getCategories().add(mexicanCategory); + + recipes.add(tacosRecipe); + return recipes; + } +} diff --git a/src/main/java/guru/springframework/controllers/IndexController.java b/src/main/java/guru/springframework/controllers/IndexController.java index 3b0bf4b219..f42cf269d5 100644 --- a/src/main/java/guru/springframework/controllers/IndexController.java +++ b/src/main/java/guru/springframework/controllers/IndexController.java @@ -1,13 +1,28 @@ package guru.springframework.controllers; +import guru.springframework.domain.Category; +import guru.springframework.domain.UnitOfMeasure; +import guru.springframework.repositories.CategoryRepository; +import guru.springframework.repositories.UnitOfMeasureRepository; +import guru.springframework.services.RecipeService; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; +import java.util.Optional; + @Controller public class IndexController { + + private final RecipeService recipeService; + + public IndexController(RecipeService recipeService) { + this.recipeService = recipeService; + } + @RequestMapping({"","/","index"}) - public String getIndexPage(){ - System.out.println("Something...changed"); + public String getIndexPage(Model model){ + model.addAttribute("recipes",recipeService.getRecipes()); return "index"; //ligado a una pagina de inicio } } diff --git a/src/main/java/guru/springframework/domain/Category.java b/src/main/java/guru/springframework/domain/Category.java index 2687379502..aeba84d3e2 100644 --- a/src/main/java/guru/springframework/domain/Category.java +++ b/src/main/java/guru/springframework/domain/Category.java @@ -12,5 +12,19 @@ public class Category { @ManyToMany(mappedBy = "categories") private Set recipes; + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } } diff --git a/src/main/java/guru/springframework/domain/Ingredient.java b/src/main/java/guru/springframework/domain/Ingredient.java index 5e2c87d6c9..8b326afeae 100644 --- a/src/main/java/guru/springframework/domain/Ingredient.java +++ b/src/main/java/guru/springframework/domain/Ingredient.java @@ -17,6 +17,17 @@ public class Ingredient { @OneToOne(fetch = FetchType.EAGER) private UnitOfMeasure unitOfMeasure; + public Ingredient(){ + + } + + public Ingredient(String description, BigDecimal amount, Recipe recipe, UnitOfMeasure unitOfMeasure) { + this.description = description; + this.amount = amount; + this.recipe = recipe; + this.unitOfMeasure = unitOfMeasure; + } + public Long getId() { return id; } diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index c93829ea10..4d823f3e8f 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -1,6 +1,7 @@ package guru.springframework.domain; import javax.persistence.*; +import java.util.HashSet; import java.util.Set; @Entity @@ -15,6 +16,7 @@ public class Recipe { private Integer servings; private String source; private String url; + @Lob private String directions; //anotacion de enumeracion con valor @Enumerated(value = EnumType.STRING) @@ -26,12 +28,12 @@ public class Recipe { private Notes notes; //propiedad 1 a muchos con cascada mapeado por esta clase @OneToMany(cascade = CascadeType.ALL, mappedBy = "recipe") - private Set ingredients; + private Set ingredients = new HashSet<>(); @ManyToMany @JoinTable(name = "recipe_category", joinColumns = @JoinColumn(name = "recipe_id"), inverseJoinColumns = @JoinColumn(name = "category_id")) - private Set categories; + private Set categories = new HashSet<>(); public Long getId() { return id; diff --git a/src/main/java/guru/springframework/domain/UnitOfMeasure.java b/src/main/java/guru/springframework/domain/UnitOfMeasure.java index 06a6de7a08..b906e9aa7f 100644 --- a/src/main/java/guru/springframework/domain/UnitOfMeasure.java +++ b/src/main/java/guru/springframework/domain/UnitOfMeasure.java @@ -7,7 +7,7 @@ public class UnitOfMeasure { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String uom; + private String description; //relación con Ingredient @OneToOne private Ingredient ingredient; @@ -20,12 +20,12 @@ public void setId(Long id) { this.id = id; } - public String getUom() { - return uom; + public String getDescription() { + return description; } - public void setUom(String uom) { - this.uom = uom; + public void setDescription(String description) { + this.description = description; } public Ingredient getIngredient() { diff --git a/src/main/java/guru/springframework/repositories/CategoryRepository.java b/src/main/java/guru/springframework/repositories/CategoryRepository.java index f289bb3978..ab7c53e194 100644 --- a/src/main/java/guru/springframework/repositories/CategoryRepository.java +++ b/src/main/java/guru/springframework/repositories/CategoryRepository.java @@ -3,6 +3,10 @@ import guru.springframework.domain.Category; import org.springframework.data.repository.CrudRepository; +import java.util.Optional; + public interface CategoryRepository extends CrudRepository { + Optional findByDescription(String description); + } diff --git a/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java b/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java index aca01fcdbb..15cb9a4046 100644 --- a/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java +++ b/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java @@ -3,5 +3,8 @@ import guru.springframework.domain.UnitOfMeasure; import org.springframework.data.repository.CrudRepository; +import java.util.Optional; + public interface UnitOfMeasureRepository extends CrudRepository { + Optional findByDescription(String description); } diff --git a/src/main/java/guru/springframework/services/RecipeService.java b/src/main/java/guru/springframework/services/RecipeService.java new file mode 100644 index 0000000000..d175229f82 --- /dev/null +++ b/src/main/java/guru/springframework/services/RecipeService.java @@ -0,0 +1,10 @@ +package guru.springframework.services; + +import guru.springframework.domain.Recipe; +import org.springframework.stereotype.Service; + +import java.util.Set; +@Service +public interface RecipeService { + Set getRecipes(); +} diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java new file mode 100644 index 0000000000..3ddd1bd17e --- /dev/null +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -0,0 +1,23 @@ +package guru.springframework.services; + +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; +@Service +public class RecipeServiceImpl implements RecipeService{ + private final RecipeRepository recipeRepository; + + public RecipeServiceImpl(RecipeRepository recipeRepository) { + this.recipeRepository = recipeRepository; + } + + @Override + public Set getRecipes() { + Set recipeSet = new HashSet<>(); + recipeRepository.findAll().iterator().forEachRemaining(recipeSet::add); + return recipeSet; + } +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000000..de968881cb --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,12 @@ +INSERT INTO category (description) VALUES ('American'); +INSERT INTO category (description) VALUES ('Mexican'); +INSERT INTO category (description) VALUES ('Italian'); +INSERT INTO category (description) VALUES ('Fast Food'); +INSERT INTO unit_of_measure (description) VALUES ('Teaspoon'); +INSERT INTO unit_of_measure (description) VALUES ('Tablespoon'); +INSERT INTO unit_of_measure (description) VALUES ('Cup'); +INSERT INTO unit_of_measure (description) VALUES ('Pinch'); +INSERT INTO unit_of_measure (description) VALUES ('Ounce'); +INSERT INTO unit_of_measure (description) VALUES ('Each'); +INSERT INTO unit_of_measure (description) VALUES ('Dash'); +INSERT INTO unit_of_measure (description) VALUES ('Pint'); \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 9f865db6bc..46a9423af5 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -6,5 +6,16 @@

My Recipes! 44

+ + + + + + + + + + +
IDDescription
123Tasty
\ No newline at end of file From efd3775b006c9212de234276c6ef3451f7359d1b Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 13 Jul 2023 11:45:56 -0600 Subject: [PATCH 04/36] Refactoring convenient methods: Ingredients, Recipes and bootstrap --- .../bootstrap/RecipeBootstrap.java | 38 +++++++++---------- .../springframework/domain/Ingredient.java | 6 +++ .../guru/springframework/domain/Recipe.java | 7 +++- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java index 071b24f1bc..6c05679b4f 100644 --- a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java +++ b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java @@ -95,16 +95,15 @@ private List getRecipes(){ guacNotes.setRecipeNotes("Chilling tomatoes hurts their flavor.\n" + "So, if you want to add chopped tomato to your guacamole, add it just before serving.\n" + "Read more: https://www.simplyrecipes.com/recipes/perfect_guacamole/"); - guacNotes.setRecipe(guacRecipe); guacRecipe.setNotes(guacNotes); - guacRecipe.getIngredients().add(new Ingredient("Ripe Avocados",new BigDecimal(2), guacRecipe, eachUom)); - guacRecipe.getIngredients().add(new Ingredient("Kosher salt",new BigDecimal(0.5),guacRecipe, teaspoonUOM)); - guacRecipe.getIngredients().add(new Ingredient("Fresh lime/lemon juice",new BigDecimal(2),guacRecipe, tableSpoonUOM)); - guacRecipe.getIngredients().add(new Ingredient("Minced Red/green onion ",new BigDecimal(2),guacRecipe, tableSpoonUOM)); - guacRecipe.getIngredients().add(new Ingredient("Serrano chiles, stems and seeds removed",new BigDecimal(2),guacRecipe, eachUom)); - guacRecipe.getIngredients().add(new Ingredient("Cilantro",new BigDecimal(2),guacRecipe, tableSpoonUOM)); - guacRecipe.getIngredients().add(new Ingredient("Freshly grated black pepper",new BigDecimal(2),guacRecipe, dashUom)); - guacRecipe.getIngredients().add(new Ingredient("Ripe tomato, seeds and pulp removed",new BigDecimal(0.5),guacRecipe, eachUom)); + guacRecipe.addIngredient(new Ingredient("Ripe Avocados",new BigDecimal(2), eachUom)); + guacRecipe.addIngredient(new Ingredient("Kosher salt",new BigDecimal(0.5), teaspoonUOM)); + guacRecipe.addIngredient(new Ingredient("Fresh lime/lemon juice",new BigDecimal(2), tableSpoonUOM)); + guacRecipe.addIngredient(new Ingredient("Minced Red/green onion ",new BigDecimal(2), tableSpoonUOM)); + guacRecipe.addIngredient(new Ingredient("Serrano chiles, stems and seeds removed",new BigDecimal(2), eachUom)); + guacRecipe.addIngredient(new Ingredient("Cilantro",new BigDecimal(2),tableSpoonUOM)); + guacRecipe.addIngredient(new Ingredient("Freshly grated black pepper",new BigDecimal(2), dashUom)); + guacRecipe.addIngredient(new Ingredient("Ripe tomato, seeds and pulp removed",new BigDecimal(0.5), eachUom)); guacRecipe.getCategories().add(americanCategory); guacRecipe.getCategories().add(mexicanCategory); @@ -128,19 +127,18 @@ private List getRecipes(){ Notes tacoNotes = new Notes(); tacoNotes.setRecipeNotes("Look for ancho chile powder with the Mexican ingredients at your grocery store, on buy it online. (If you can't find ancho chili powder, you replace the ancho chili, the oregano, and the cumin with 2 1/2 tablespoons regular chili powder, though the flavor won't be quite the same.)\n" + "Read more: https://www.simplyrecipes.com/recipes/spicy_grilled_chicken_tacos/\n"); - tacoNotes.setRecipe(tacosRecipe); tacosRecipe.setNotes(tacoNotes); - tacosRecipe.getIngredients().add(new Ingredient("Ancho chilli powder", new BigDecimal(2), tacosRecipe, tableSpoonUOM)); - tacosRecipe.getIngredients().add(new Ingredient("Dried oregano", new BigDecimal(1), tacosRecipe, teaspoonUOM)); - tacosRecipe.getIngredients().add(new Ingredient("Dried Cumin", new BigDecimal(1), tacosRecipe, teaspoonUOM)); - tacosRecipe.getIngredients().add(new Ingredient("Salt", new BigDecimal(0.5), tacosRecipe, teaspoonUOM)); - tacosRecipe.getIngredients().add(new Ingredient("Corn tortillas", new BigDecimal(8), tacosRecipe, eachUom)); - tacosRecipe.getIngredients().add(new Ingredient("Avocado, sliced", new BigDecimal(2), tacosRecipe, eachUom)); - tacosRecipe.getIngredients().add(new Ingredient("Boneless Chicken thighs", new BigDecimal(6), tacosRecipe, eachUom)); - tacosRecipe.getIngredients().add(new Ingredient("Onion, sliced", new BigDecimal(0.25), tacosRecipe, eachUom)); - tacosRecipe.getIngredients().add(new Ingredient("Sour cream", new BigDecimal(0.5), tacosRecipe, cupsUom)); - tacosRecipe.getIngredients().add(new Ingredient("Lime/lemon", new BigDecimal(1), tacosRecipe, eachUom)); + tacosRecipe.addIngredient(new Ingredient("Ancho chilli powder", new BigDecimal(2), tableSpoonUOM)); + tacosRecipe.addIngredient(new Ingredient("Dried oregano", new BigDecimal(1), teaspoonUOM)); + tacosRecipe.addIngredient(new Ingredient("Dried Cumin", new BigDecimal(1), teaspoonUOM)); + tacosRecipe.addIngredient(new Ingredient("Salt", new BigDecimal(0.5), teaspoonUOM)); + tacosRecipe.addIngredient(new Ingredient("Corn tortillas", new BigDecimal(8), eachUom)); + tacosRecipe.addIngredient(new Ingredient("Avocado, sliced", new BigDecimal(2), eachUom)); + tacosRecipe.addIngredient(new Ingredient("Boneless Chicken thighs", new BigDecimal(6), eachUom)); + tacosRecipe.addIngredient(new Ingredient("Onion, sliced", new BigDecimal(0.25), eachUom)); + tacosRecipe.addIngredient(new Ingredient("Sour cream", new BigDecimal(0.5), cupsUom)); + tacosRecipe.addIngredient(new Ingredient("Lime/lemon", new BigDecimal(1), eachUom)); tacosRecipe.getCategories().add(americanCategory); tacosRecipe.getCategories().add(mexicanCategory); diff --git a/src/main/java/guru/springframework/domain/Ingredient.java b/src/main/java/guru/springframework/domain/Ingredient.java index 8b326afeae..2fac434a29 100644 --- a/src/main/java/guru/springframework/domain/Ingredient.java +++ b/src/main/java/guru/springframework/domain/Ingredient.java @@ -21,6 +21,12 @@ public Ingredient(){ } + public Ingredient(String description, BigDecimal amount, UnitOfMeasure unitOfMeasure) { + this.description = description; + this.amount = amount; + this.unitOfMeasure = unitOfMeasure; + } + public Ingredient(String description, BigDecimal amount, Recipe recipe, UnitOfMeasure unitOfMeasure) { this.description = description; this.amount = amount; diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index 4d823f3e8f..be57247f74 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -121,8 +121,13 @@ public Notes getNotes() { public void setNotes(Notes notes) { this.notes = notes; + notes.setRecipe(this); + } + public Recipe addIngredient(Ingredient ingredient){ + ingredient.setRecipe(this); + this.ingredients.add(ingredient); + return this; } - public Set getIngredients() { return ingredients; } From 2368bfb40b4d0c7116492182a54ce30a469ee1d0 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 22 Sep 2023 12:41:19 -0600 Subject: [PATCH 05/36] Committed Lombok stuff --- pom.xml | 4 ++ .../bootstrap/RecipeBootstrap.java | 6 +- .../controllers/IndexController.java | 4 +- .../guru/springframework/domain/Category.java | 19 ++--- .../springframework/domain/Ingredient.java | 54 ++------------ .../guru/springframework/domain/Notes.java | 29 ++------ .../guru/springframework/domain/Recipe.java | 71 +------------------ .../springframework/domain/UnitOfMeasure.java | 31 +------- .../services/RecipeServiceImpl.java | 5 ++ 9 files changed, 37 insertions(+), 186 deletions(-) diff --git a/pom.xml b/pom.xml index 25bb466d7f..1e09fe5519 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,10 @@ h2 runtime + + org.projectlombok + lombok + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java index 6c05679b4f..18f7f80504 100644 --- a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java +++ b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java @@ -4,16 +4,18 @@ import guru.springframework.repositories.CategoryRepository; import guru.springframework.repositories.RecipeRepository; import guru.springframework.repositories.UnitOfMeasureRepository; +import lombok.extern.slf4j.Slf4j; import org.aspectj.weaver.ast.Not; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; +import javax.transaction.Transactional; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Optional; - +@Slf4j @Component public class RecipeBootstrap implements ApplicationListener { private final CategoryRepository categoryRepository; @@ -27,8 +29,10 @@ public RecipeBootstrap(CategoryRepository categoryRepository, RecipeRepository r } //guardar lo que viene de los repositorios @Override + @Transactional public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { recipeRepository.saveAll(getRecipes()); + log.debug("Loading bootstrap data..."); } private List getRecipes(){ diff --git a/src/main/java/guru/springframework/controllers/IndexController.java b/src/main/java/guru/springframework/controllers/IndexController.java index f42cf269d5..41c272dbc9 100644 --- a/src/main/java/guru/springframework/controllers/IndexController.java +++ b/src/main/java/guru/springframework/controllers/IndexController.java @@ -5,12 +5,13 @@ import guru.springframework.repositories.CategoryRepository; import guru.springframework.repositories.UnitOfMeasureRepository; import guru.springframework.services.RecipeService; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import java.util.Optional; - +@Slf4j @Controller public class IndexController { @@ -22,6 +23,7 @@ public IndexController(RecipeService recipeService) { @RequestMapping({"","/","index"}) public String getIndexPage(Model model){ + log.debug("Getting index page"); model.addAttribute("recipes",recipeService.getRecipes()); return "index"; //ligado a una pagina de inicio } diff --git a/src/main/java/guru/springframework/domain/Category.java b/src/main/java/guru/springframework/domain/Category.java index aeba84d3e2..4942026119 100644 --- a/src/main/java/guru/springframework/domain/Category.java +++ b/src/main/java/guru/springframework/domain/Category.java @@ -1,8 +1,12 @@ package guru.springframework.domain; +import lombok.*; + import javax.persistence.*; import java.util.Set; +@Data +@EqualsAndHashCode(exclude = {"recipes"}) @Entity public class Category { @Id @@ -12,19 +16,4 @@ public class Category { @ManyToMany(mappedBy = "categories") private Set recipes; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } } diff --git a/src/main/java/guru/springframework/domain/Ingredient.java b/src/main/java/guru/springframework/domain/Ingredient.java index 2fac434a29..13de1a55f7 100644 --- a/src/main/java/guru/springframework/domain/Ingredient.java +++ b/src/main/java/guru/springframework/domain/Ingredient.java @@ -1,8 +1,12 @@ package guru.springframework.domain; +import lombok.Data; +import lombok.EqualsAndHashCode; + import javax.persistence.*; import java.math.BigDecimal; - +@Data +@EqualsAndHashCode(exclude = {"recipe"}) @Entity public class Ingredient { @Id @@ -20,57 +24,9 @@ public class Ingredient { public Ingredient(){ } - public Ingredient(String description, BigDecimal amount, UnitOfMeasure unitOfMeasure) { this.description = description; this.amount = amount; this.unitOfMeasure = unitOfMeasure; } - - public Ingredient(String description, BigDecimal amount, Recipe recipe, UnitOfMeasure unitOfMeasure) { - this.description = description; - this.amount = amount; - this.recipe = recipe; - this.unitOfMeasure = unitOfMeasure; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public BigDecimal getAmount() { - return amount; - } - - public void setAmount(BigDecimal amount) { - this.amount = amount; - } - - public Recipe getRecipe() { - return recipe; - } - - public void setRecipe(Recipe recipe) { - this.recipe = recipe; - } - - public UnitOfMeasure getUnitOfMeasure() { - return unitOfMeasure; - } - - public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) { - this.unitOfMeasure = unitOfMeasure; - } } diff --git a/src/main/java/guru/springframework/domain/Notes.java b/src/main/java/guru/springframework/domain/Notes.java index cb5269b11a..c7374cc4a0 100644 --- a/src/main/java/guru/springframework/domain/Notes.java +++ b/src/main/java/guru/springframework/domain/Notes.java @@ -1,7 +1,11 @@ package guru.springframework.domain; -import javax.persistence.*; +import lombok.Data; +import lombok.EqualsAndHashCode; +import javax.persistence.*; +@Data +@EqualsAndHashCode(exclude = {"recipe"}) @Entity public class Notes { //primary key @@ -15,27 +19,4 @@ public class Notes { @Lob private String recipeNotes; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Recipe getRecipe() { - return recipe; - } - - public void setRecipe(Recipe recipe) { - this.recipe = recipe; - } - - public String getRecipeNotes() { - return recipeNotes; - } - - public void setRecipeNotes(String recipeNotes) { - this.recipeNotes = recipeNotes; - } } diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index be57247f74..12a33cd980 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -1,9 +1,11 @@ package guru.springframework.domain; +import lombok.Data; + import javax.persistence.*; import java.util.HashSet; import java.util.Set; - +@Data @Entity public class Recipe { //primary key generada con la estrategia de identidad @@ -39,10 +41,6 @@ public Long getId() { return id; } - public void setId(Long id) { - this.id = id; - } - public String getDescription() { return description; } @@ -51,74 +49,22 @@ public void setDescription(String description) { this.description = description; } - public Integer getPrepTime() { - return prepTime; - } - public void setPrepTime(Integer prepTime) { this.prepTime = prepTime; } - public Integer getCookTime() { - return cookTime; - } - public void setCookTime(Integer cookTime) { this.cookTime = cookTime; } - public Integer getServings() { - return servings; - } - - public void setServings(Integer servings) { - this.servings = servings; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getDirections() { - return directions; - } - public void setDirections(String directions) { this.directions = directions; } - public Difficulty getDifficulty() { - return difficulty; - } - public void setDifficulty(Difficulty difficulty) { this.difficulty = difficulty; } - public Byte[] getImage() { - return image; - } - - public void setImage(Byte[] image) { - this.image = image; - } - - public Notes getNotes() { - return notes; - } - public void setNotes(Notes notes) { this.notes = notes; notes.setRecipe(this); @@ -128,19 +74,8 @@ public Recipe addIngredient(Ingredient ingredient){ this.ingredients.add(ingredient); return this; } - public Set getIngredients() { - return ingredients; - } - - public void setIngredients(Set ingredients) { - this.ingredients = ingredients; - } - public Set getCategories() { return categories; } - public void setCategories(Set categories) { - this.categories = categories; - } } diff --git a/src/main/java/guru/springframework/domain/UnitOfMeasure.java b/src/main/java/guru/springframework/domain/UnitOfMeasure.java index b906e9aa7f..89294611a6 100644 --- a/src/main/java/guru/springframework/domain/UnitOfMeasure.java +++ b/src/main/java/guru/springframework/domain/UnitOfMeasure.java @@ -1,38 +1,13 @@ package guru.springframework.domain; -import javax.persistence.*; +import lombok.Data; +import javax.persistence.*; +@Data @Entity public class UnitOfMeasure { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String description; - //relación con Ingredient - @OneToOne - private Ingredient ingredient; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Ingredient getIngredient() { - return ingredient; - } - - public void setIngredient(Ingredient ingredient) { - this.ingredient = ingredient; - } } diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index 3ddd1bd17e..2cfceaf0ff 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -2,10 +2,13 @@ import guru.springframework.domain.Recipe; import guru.springframework.repositories.RecipeRepository; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.Set; +@Slf4j @Service public class RecipeServiceImpl implements RecipeService{ private final RecipeRepository recipeRepository; @@ -16,6 +19,8 @@ public RecipeServiceImpl(RecipeRepository recipeRepository) { @Override public Set getRecipes() { + log.debug("I'm in the service"); + Set recipeSet = new HashSet<>(); recipeRepository.findAll().iterator().forEachRemaining(recipeSet::add); return recipeSet; From f9cfb5e6982f4866e57b84c9a7edf93ba2bbfc95 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Mon, 25 Sep 2023 11:23:41 -0600 Subject: [PATCH 06/36] Commit Panel Interface --- src/main/resources/templates/index.html | 50 +++++++++++++++++++------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 46a9423af5..5844e0f338 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -3,19 +3,45 @@ Recipe Home + -

My Recipes! 44

- - - - - - - - - - -
IDDescription
123Tasty
+
+
+
+
+
+

My Recipes!

+
+
+
+ + + + + + + + + + + + + + + + + + + +
IDDescription
123Tasty
123Spicy
123Sweet
+
+
+
+
+
+
\ No newline at end of file From ffd67cc8be8dff2c40ff2a5a0662ae21c6d98ef0 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Mon, 25 Sep 2023 11:51:44 -0600 Subject: [PATCH 07/36] Unit Test using Mockito --- .../springframework/domain/CategoryTest.java | 31 ++++++++++++++ .../services/RecipeServiceImplTest.java | 40 +++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/test/java/guru/springframework/domain/CategoryTest.java create mode 100644 src/test/java/guru/springframework/services/RecipeServiceImplTest.java diff --git a/src/test/java/guru/springframework/domain/CategoryTest.java b/src/test/java/guru/springframework/domain/CategoryTest.java new file mode 100644 index 0000000000..c63db5bab2 --- /dev/null +++ b/src/test/java/guru/springframework/domain/CategoryTest.java @@ -0,0 +1,31 @@ +package guru.springframework.domain; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CategoryTest { + Category category; + + //setup + @Before + public void setUp(){ + category = new Category(); + } + + @Test + public void getId() { + Long idValue = 4L; + category.setId(idValue); + assertEquals(idValue,category.getId()); + } + + @Test + public void getDescription() { + } + + @Test + public void getRecipes() { + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java new file mode 100644 index 0000000000..85697bcc14 --- /dev/null +++ b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java @@ -0,0 +1,40 @@ +package guru.springframework.services; + +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class RecipeServiceImplTest { + RecipeServiceImpl recipeService; + @Mock + RecipeRepository recipeRepository; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + recipeService = new RecipeServiceImpl(recipeRepository); + } + + @Test + public void getRecipes() { + Recipe recipe = new Recipe(); + HashSet recipesData = new HashSet<>(); + recipesData.add(recipe); + + when(recipeService.getRecipes()).thenReturn(recipesData); + + Set recipes = recipeService.getRecipes(); + assertEquals(recipes.size(),1); + verify(recipeRepository,times(1)).findAll(); + } +} \ No newline at end of file From 796018545aefc6a96e4144f5476c1fc3b9d16807 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Mon, 25 Sep 2023 12:00:13 -0600 Subject: [PATCH 08/36] Assignment: Unit Testing the Index Controller --- .../controllers/IndexControllerTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/test/java/guru/springframework/controllers/IndexControllerTest.java diff --git a/src/test/java/guru/springframework/controllers/IndexControllerTest.java b/src/test/java/guru/springframework/controllers/IndexControllerTest.java new file mode 100644 index 0000000000..5d1563dcf8 --- /dev/null +++ b/src/test/java/guru/springframework/controllers/IndexControllerTest.java @@ -0,0 +1,33 @@ +package guru.springframework.controllers; + +import guru.springframework.services.RecipeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.ui.Model; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class IndexControllerTest { + @Mock + RecipeService recipeService; + @Mock + Model model; + IndexController controller; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + controller = new IndexController(recipeService); + } + + @Test + public void getIndexPage() { + String viewName = controller.getIndexPage(model); + assertEquals("index",viewName); + verify(recipeService,times(1)).getRecipes(); + verify(model,times(1)).addAttribute(eq("recipes"),anySet()); + } +} \ No newline at end of file From f5233eaba20118a5507f0e029097075e2a156a36 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Mon, 25 Sep 2023 12:17:32 -0600 Subject: [PATCH 09/36] Just adding Tests --- .../controllers/IndexControllerTest.java | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/test/java/guru/springframework/controllers/IndexControllerTest.java b/src/test/java/guru/springframework/controllers/IndexControllerTest.java index 5d1563dcf8..a9a8b7baed 100644 --- a/src/test/java/guru/springframework/controllers/IndexControllerTest.java +++ b/src/test/java/guru/springframework/controllers/IndexControllerTest.java @@ -1,12 +1,21 @@ package guru.springframework.controllers; +import guru.springframework.domain.Recipe; import guru.springframework.services.RecipeService; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.ui.Model; +import java.util.HashSet; +import java.util.Set; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -24,10 +33,33 @@ public void setUp() throws Exception { } @Test - public void getIndexPage() { + public void testMockMVC() throws Exception{ + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + mockMvc.perform(MockMvcRequestBuilders.get("/")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("index")); + + + } + + @Test + public void getIndexPage() throws Exception { + Set recipes =new HashSet<>(); + recipes.add(new Recipe()); + Recipe recipe = new Recipe(); + recipe.setId(1L); + recipes.add(recipe); + + when(recipeService.getRecipes()).thenReturn(recipes); + + ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(Set.class); + String viewName = controller.getIndexPage(model); assertEquals("index",viewName); verify(recipeService,times(1)).getRecipes(); - verify(model,times(1)).addAttribute(eq("recipes"),anySet()); + verify(model,times(1)).addAttribute(eq("recipes"),argumentCaptor.capture()); + + Set setInController = argumentCaptor.getValue(); + assertEquals(2,setInController.size()); } } \ No newline at end of file From c95346e04263c6ac05af357289083fc4f11af907 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 28 Sep 2023 11:12:30 -0600 Subject: [PATCH 10/36] Updating the Recipe Repo and Recipe Service Impl, added the Recipe Controller and the show.html document. For those added some tests --- pom.xml | 38 +++++ .../controllers/RecipeController.java | 21 +++ .../guru/springframework/domain/Recipe.java | 33 ----- .../services/RecipeService.java | 1 + .../services/RecipeServiceImpl.java | 12 ++ src/main/resources/templates/index.html | 29 ++-- src/main/resources/templates/recipe/show.html | 133 ++++++++++++++++++ .../controllers/RecipeControllerTest.java | 42 ++++++ .../UnitOfMeasureRepositoryIT.java | 28 ++++ .../services/RecipeServiceImplTest.java | 19 ++- 10 files changed, 314 insertions(+), 42 deletions(-) create mode 100644 src/main/java/guru/springframework/controllers/RecipeController.java create mode 100644 src/main/resources/templates/recipe/show.html create mode 100644 src/test/java/guru/springframework/controllers/RecipeControllerTest.java create mode 100644 src/test/java/guru/springframework/repositories/UnitOfMeasureRepositoryIT.java diff --git a/pom.xml b/pom.xml index 1e09fe5519..14cccaa1cc 100644 --- a/pom.xml +++ b/pom.xml @@ -52,11 +52,27 @@ org.projectlombok lombok
+ + org.webjars + bootstrap + 5.2.3 + + + org.webjars + jquery + 3.6.4 + org.springframework.boot spring-boot-starter-test test + + org.junit.jupiter + junit-jupiter + RELEASE + test + @@ -65,6 +81,28 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-failsafe-plugin + 2.20 + + + **/*.IT.java + + + ${basedir}/target/classes + + none + + + + + integration-test + verify + + + + diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java new file mode 100644 index 0000000000..d16de4a93c --- /dev/null +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -0,0 +1,21 @@ +package guru.springframework.controllers; + +import guru.springframework.services.RecipeService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class RecipeController { + private final RecipeService recipeService; + + public RecipeController(RecipeService recipeService) { + this.recipeService = recipeService; + } + @RequestMapping("/recipe/show/{id}") + public String showById(@PathVariable String id, Model model){ + model.addAttribute("recipe",recipeService.findById(Long.valueOf(id))); + return "recipe/show"; + } +} diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index 12a33cd980..2342262b4a 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -36,35 +36,6 @@ public class Recipe { joinColumns = @JoinColumn(name = "recipe_id"), inverseJoinColumns = @JoinColumn(name = "category_id")) private Set categories = new HashSet<>(); - - public Long getId() { - return id; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public void setPrepTime(Integer prepTime) { - this.prepTime = prepTime; - } - - public void setCookTime(Integer cookTime) { - this.cookTime = cookTime; - } - - public void setDirections(String directions) { - this.directions = directions; - } - - public void setDifficulty(Difficulty difficulty) { - this.difficulty = difficulty; - } - public void setNotes(Notes notes) { this.notes = notes; notes.setRecipe(this); @@ -74,8 +45,4 @@ public Recipe addIngredient(Ingredient ingredient){ this.ingredients.add(ingredient); return this; } - public Set getCategories() { - return categories; - } - } diff --git a/src/main/java/guru/springframework/services/RecipeService.java b/src/main/java/guru/springframework/services/RecipeService.java index d175229f82..b6cf997a1b 100644 --- a/src/main/java/guru/springframework/services/RecipeService.java +++ b/src/main/java/guru/springframework/services/RecipeService.java @@ -7,4 +7,5 @@ @Service public interface RecipeService { Set getRecipes(); + Recipe findById(Long l); } diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index 2cfceaf0ff..35a228c51e 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -7,6 +7,7 @@ import org.springframework.stereotype.Service; import java.util.HashSet; +import java.util.Optional; import java.util.Set; @Slf4j @Service @@ -25,4 +26,15 @@ public Set getRecipes() { recipeRepository.findAll().iterator().forEachRemaining(recipeSet::add); return recipeSet; } + + @Override + public Recipe findById(Long l){ + Optional recipeOptional = recipeRepository.findById(l); + + if(!recipeOptional.isPresent()){ + throw new RuntimeException("Recipe not found!"); + } + + return recipeOptional.get(); + } } diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 5844e0f338..b2f4bf8d93 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -3,9 +3,17 @@ Recipe Home - + + + + +
@@ -22,19 +30,24 @@

My Recipes!

ID Description + View - 123 - Tasty + 123 + Tasty + View - 123 - Spicy + 345 + Spicy + View - 123 + + 678 Sweet + View
diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html new file mode 100644 index 0000000000..ddd9a5cbf7 --- /dev/null +++ b/src/main/resources/templates/recipe/show.html @@ -0,0 +1,133 @@ + + + + + Show Recipe + + + + + + + + +
+
+
+
+
+
+ Recipe Description Here! +
+
+
+
+ Categories: +
+
+
    +
  • cat one
  • +
  • cat two
  • +
  • cat three
  • +
+
+
+
+
+ Prep Time: +
+
+

30 min

+
+
+ Difficulty: +
+
+

Easy

+
+
+
+
+ Cooktime: +
+
+

30 min

+
+
+ Servings: +
+
+

4

+
+
+
+
+ Source: +
+
+

30 min

+
+
+ URL: +
+
+

http://www.example.com

+
+
+
+
+
+
+ Ingredients +
+
+
+
+
    +
  • 1 Cup of milk
  • +
  • 1 Teaspoon of chocolate
  • +
  • 1 Teaspoon of Sugar
  • +
+
+
+
+
+
+
+ Directions +
+
+
+
+

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

+
+
+
+
+
+
+ Notes +
+
+
+
+

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

+
+
+
+
+
+
+
+
+ + + diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java new file mode 100644 index 0000000000..9a19a93956 --- /dev/null +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -0,0 +1,42 @@ +package guru.springframework.controllers; + +import guru.springframework.domain.Recipe; +import guru.springframework.services.RecipeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +public class RecipeControllerTest { + @Mock + RecipeService recipeService; + RecipeController recipeController; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + recipeController = new RecipeController(recipeService); + } + + @Test + public void testGetRecipe() throws Exception { + Recipe recipe = new Recipe(); + recipe.setId(1L); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build(); + + when(recipeService.findById(anyLong())).thenReturn(recipe); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/show/1")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/show")); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/repositories/UnitOfMeasureRepositoryIT.java b/src/test/java/guru/springframework/repositories/UnitOfMeasureRepositoryIT.java new file mode 100644 index 0000000000..97b009efb5 --- /dev/null +++ b/src/test/java/guru/springframework/repositories/UnitOfMeasureRepositoryIT.java @@ -0,0 +1,28 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.UnitOfMeasure; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Optional; + +import static org.junit.Assert.*; +@RunWith(SpringRunner.class) +@DataJpaTest +public class UnitOfMeasureRepositoryIT { + @Autowired + UnitOfMeasureRepository unitOfMeasureRepository; + @Before + public void setUp() throws Exception { + } + + @Test + public void findByDescription() throws Exception{ + Optional uomOptional = unitOfMeasureRepository.findByDescription("Teaspoon"); + assertEquals("Teaspoon", uomOptional.get().getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java index 85697bcc14..e9ca2e3bb7 100644 --- a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java +++ b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java @@ -8,6 +8,7 @@ import org.mockito.MockitoAnnotations; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import static org.junit.Assert.*; @@ -26,7 +27,22 @@ public void setUp() throws Exception { } @Test - public void getRecipes() { + public void getRecipeByIdTest() throws Exception { + Recipe recipe = new Recipe(); + recipe.setId(1L); + Optional recipeOptional = Optional.of(recipe); + + when(recipeRepository.findById(anyLong())).thenReturn(recipeOptional); + + Recipe recipeReturned = recipeService.findById(1L); + + assertNotNull("Null recipe returned",recipeReturned); + verify(recipeRepository,times(1)).findById(anyLong()); + verify(recipeRepository,never()).findAll(); + } + + @Test + public void getRecipesTest() throws Exception { Recipe recipe = new Recipe(); HashSet recipesData = new HashSet<>(); recipesData.add(recipe); @@ -36,5 +52,6 @@ public void getRecipes() { Set recipes = recipeService.getRecipes(); assertEquals(recipes.size(),1); verify(recipeRepository,times(1)).findAll(); + verify(recipeRepository,never()).findById(anyLong()); } } \ No newline at end of file From 02e561d73d78db3a22a3c05b9f2010572838cdde Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 28 Sep 2023 12:09:40 -0600 Subject: [PATCH 11/36] Setting up recipe data with Thymeleaf --- .../bootstrap/RecipeBootstrap.java | 7 ++++ src/main/resources/templates/recipe/show.html | 35 +++++++++++-------- .../controllers/RecipeControllerTest.java | 3 +- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java index 18f7f80504..7129f6af11 100644 --- a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java +++ b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java @@ -111,6 +111,9 @@ private List getRecipes(){ guacRecipe.getCategories().add(americanCategory); guacRecipe.getCategories().add(mexicanCategory); + guacRecipe.setUrl("https://www.simplyrecipes.com/recipes/perfect_guacamole/"); + guacRecipe.setServings(4); + guacRecipe.setSource("Simply Recipes"); recipes.add(guacRecipe); @@ -147,6 +150,10 @@ private List getRecipes(){ tacosRecipe.getCategories().add(americanCategory); tacosRecipe.getCategories().add(mexicanCategory); + tacosRecipe.setUrl("https://www.simplyrecipes.com/recipes/spicy_grilled_chicken_tacos/"); + tacosRecipe.setServings(6); + tacosRecipe.setSource("Simply Recipes"); + recipes.add(tacosRecipe); return recipes; } diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html index ddd9a5cbf7..eb1415f7b3 100644 --- a/src/main/resources/templates/recipe/show.html +++ b/src/main/resources/templates/recipe/show.html @@ -21,6 +21,7 @@
+ Recipe Description Here!
@@ -30,9 +31,10 @@
    -
  • cat one
  • -
  • cat two
  • -
  • cat three
  • +
  • cat one
  • +
  • cat two
  • + +
  • cat three
@@ -41,13 +43,14 @@ Prep Time:
-

30 min

+ +

30 min

Difficulty:
-

Easy

+

Easy

@@ -55,13 +58,13 @@ Cooktime:
-

30 min

+

30 min

Servings:
-

4

+

4

@@ -69,13 +72,13 @@ Source:
-

30 min

+

30 min

URL:
-

http://www.example.com

+

http://www.example.com

@@ -88,9 +91,13 @@
    -
  • 1 Cup of milk
  • -
  • 1 Teaspoon of chocolate
  • -
  • 1 Teaspoon of Sugar
  • +
  • 1 Cup of milk
  • +
  • 1 Teaspoon of chocolate
  • + +
  • 1 Teaspoon of Sugar
@@ -103,7 +110,7 @@
-

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

+

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

@@ -115,7 +122,7 @@
-

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

+

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.

diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index 9a19a93956..4efba9a522 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -37,6 +37,7 @@ public void testGetRecipe() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/recipe/show/1")) .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.view().name("recipe/show")); + .andExpect(MockMvcResultMatchers.view().name("recipe/show")) + .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); } } \ No newline at end of file From 4761ee66d5d575f0bbc6167bc4a681eafe3f72ea Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Tue, 3 Oct 2023 11:35:39 -0600 Subject: [PATCH 12/36] Creating a bunch of command, converter and test files that will help to extract data from the web, convert it and save it --- .../commands/CategoryCommand.java | 13 +++ .../commands/IngredientCommand.java | 17 ++++ .../commands/NotesCommand.java | 13 +++ .../commands/RecipeCommand.java | 27 ++++++ .../commands/UnitOfMeasureCommand.java | 13 +++ .../converters/CategoryCommandToCategory.java | 24 +++++ .../converters/CategoryToCategoryCommand.java | 26 +++++ .../IngredientCommandToIngredient.java | 31 ++++++ .../IngredientToIngredientCommand.java | 33 +++++++ .../converters/NotesCommandToNotes.java | 25 +++++ .../converters/NotesToNotesCommand.java | 25 +++++ .../converters/RecipeCommandToRecipe.java | 52 ++++++++++ .../converters/RecipeToRecipeCommand.java | 53 ++++++++++ .../UnitOfMeasureCommandToUnitOfMeasure.java | 24 +++++ .../UnitOfMeasureToUnitOfMeasureCommand.java | 24 +++++ .../guru/springframework/domain/Recipe.java | 6 +- .../services/RecipeService.java | 2 + .../services/RecipeServiceImpl.java | 20 +++- .../CategoryCommandToCategoryTest.java | 41 ++++++++ .../CategoryToCategoryCommandTest.java | 40 ++++++++ .../IngredientCommandToIngredientTest.java | 73 ++++++++++++++ .../IngredientToIngredientCommandTest.java | 71 ++++++++++++++ .../converters/NotesCommandToNotesTest.java | 41 ++++++++ .../converters/NotesToNotesCommandTest.java | 40 ++++++++ .../converters/RecipeCommandToRecipeTest.java | 97 +++++++++++++++++++ .../converters/RecipeToRecipeCommandTest.java | 93 ++++++++++++++++++ ...itOfMeasureCommandToUnitOfMeasureTest.java | 41 ++++++++ ...itOfMeasureToUnitOfMeasureCommandTest.java | 39 ++++++++ .../services/RecipeServiceImplTest.java | 10 +- .../services/RecipeServiceTest.java | 57 +++++++++++ 30 files changed, 1066 insertions(+), 5 deletions(-) create mode 100644 src/main/java/guru/springframework/commands/CategoryCommand.java create mode 100644 src/main/java/guru/springframework/commands/IngredientCommand.java create mode 100644 src/main/java/guru/springframework/commands/NotesCommand.java create mode 100644 src/main/java/guru/springframework/commands/RecipeCommand.java create mode 100644 src/main/java/guru/springframework/commands/UnitOfMeasureCommand.java create mode 100644 src/main/java/guru/springframework/converters/CategoryCommandToCategory.java create mode 100644 src/main/java/guru/springframework/converters/CategoryToCategoryCommand.java create mode 100644 src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java create mode 100644 src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java create mode 100644 src/main/java/guru/springframework/converters/NotesCommandToNotes.java create mode 100644 src/main/java/guru/springframework/converters/NotesToNotesCommand.java create mode 100644 src/main/java/guru/springframework/converters/RecipeCommandToRecipe.java create mode 100644 src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java create mode 100644 src/main/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasure.java create mode 100644 src/main/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommand.java create mode 100644 src/test/java/guru/springframework/converters/CategoryCommandToCategoryTest.java create mode 100644 src/test/java/guru/springframework/converters/CategoryToCategoryCommandTest.java create mode 100644 src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java create mode 100644 src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java create mode 100644 src/test/java/guru/springframework/converters/NotesCommandToNotesTest.java create mode 100644 src/test/java/guru/springframework/converters/NotesToNotesCommandTest.java create mode 100644 src/test/java/guru/springframework/converters/RecipeCommandToRecipeTest.java create mode 100644 src/test/java/guru/springframework/converters/RecipeToRecipeCommandTest.java create mode 100644 src/test/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasureTest.java create mode 100644 src/test/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommandTest.java create mode 100644 src/test/java/guru/springframework/services/RecipeServiceTest.java diff --git a/src/main/java/guru/springframework/commands/CategoryCommand.java b/src/main/java/guru/springframework/commands/CategoryCommand.java new file mode 100644 index 0000000000..7263928369 --- /dev/null +++ b/src/main/java/guru/springframework/commands/CategoryCommand.java @@ -0,0 +1,13 @@ +package guru.springframework.commands; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +public class CategoryCommand { + private Long id; + private String description; +} diff --git a/src/main/java/guru/springframework/commands/IngredientCommand.java b/src/main/java/guru/springframework/commands/IngredientCommand.java new file mode 100644 index 0000000000..85418c951a --- /dev/null +++ b/src/main/java/guru/springframework/commands/IngredientCommand.java @@ -0,0 +1,17 @@ +package guru.springframework.commands; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; + +@Getter +@Setter +@NoArgsConstructor +public class IngredientCommand { + private Long id; + private String description; + private BigDecimal amount; + private UnitOfMeasureCommand unitOfMeasure; +} diff --git a/src/main/java/guru/springframework/commands/NotesCommand.java b/src/main/java/guru/springframework/commands/NotesCommand.java new file mode 100644 index 0000000000..60fc9314b1 --- /dev/null +++ b/src/main/java/guru/springframework/commands/NotesCommand.java @@ -0,0 +1,13 @@ +package guru.springframework.commands; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class NotesCommand { + private Long id; + private String recipeNotes; +} diff --git a/src/main/java/guru/springframework/commands/RecipeCommand.java b/src/main/java/guru/springframework/commands/RecipeCommand.java new file mode 100644 index 0000000000..b14412f9ab --- /dev/null +++ b/src/main/java/guru/springframework/commands/RecipeCommand.java @@ -0,0 +1,27 @@ +package guru.springframework.commands; + +import guru.springframework.domain.Difficulty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.HashSet; +import java.util.Set; + +@Getter +@Setter +@NoArgsConstructor +public class RecipeCommand { + private Long id; + private String description; + private Integer prepTime; + private Integer cookTime; + private Integer servings; + private String source; + private String url; + private String directions; + private Set ingredients = new HashSet<>(); + private Difficulty difficulty; + private NotesCommand notes; + private Set categories = new HashSet<>(); +} diff --git a/src/main/java/guru/springframework/commands/UnitOfMeasureCommand.java b/src/main/java/guru/springframework/commands/UnitOfMeasureCommand.java new file mode 100644 index 0000000000..1e7ff68751 --- /dev/null +++ b/src/main/java/guru/springframework/commands/UnitOfMeasureCommand.java @@ -0,0 +1,13 @@ +package guru.springframework.commands; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +public class UnitOfMeasureCommand { + private Long id; + private String description; +} diff --git a/src/main/java/guru/springframework/converters/CategoryCommandToCategory.java b/src/main/java/guru/springframework/converters/CategoryCommandToCategory.java new file mode 100644 index 0000000000..2cf483e911 --- /dev/null +++ b/src/main/java/guru/springframework/converters/CategoryCommandToCategory.java @@ -0,0 +1,24 @@ +package guru.springframework.converters; + +import guru.springframework.commands.CategoryCommand; +import guru.springframework.domain.Category; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class CategoryCommandToCategory implements Converter { + @Synchronized + @Nullable + @Override + public Category convert(CategoryCommand source){ + if(source == null){ + return null; + } + final Category category = new Category(); + category.setId(source.getId()); + category.setDescription((source.getDescription())); + return category; + } +} diff --git a/src/main/java/guru/springframework/converters/CategoryToCategoryCommand.java b/src/main/java/guru/springframework/converters/CategoryToCategoryCommand.java new file mode 100644 index 0000000000..55408cec25 --- /dev/null +++ b/src/main/java/guru/springframework/converters/CategoryToCategoryCommand.java @@ -0,0 +1,26 @@ +package guru.springframework.converters; + +import guru.springframework.commands.CategoryCommand; +import guru.springframework.domain.Category; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class CategoryToCategoryCommand implements Converter { + @Synchronized + @Nullable + @Override + public CategoryCommand convert(Category source){ + if(source == null){ + return null; + } + final CategoryCommand categoryCommand = new CategoryCommand(); + + categoryCommand.setId(source.getId()); + categoryCommand.setDescription(source.getDescription()); + + return categoryCommand; + } +} diff --git a/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java b/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java new file mode 100644 index 0000000000..53ff6b3028 --- /dev/null +++ b/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java @@ -0,0 +1,31 @@ +package guru.springframework.converters; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.domain.Ingredient; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class IngredientCommandToIngredient implements Converter { + private final UnitOfMeasureCommandToUnitOfMeasure uomConverter; + + public IngredientCommandToIngredient(UnitOfMeasureCommandToUnitOfMeasure uomConverter){ + this.uomConverter = uomConverter; + } + + @Nullable + @Override + public Ingredient convert(IngredientCommand source){ + if(source == null){ + return null; + } + + final Ingredient ingredient = new Ingredient(); + ingredient.setId(source.getId()); + ingredient.setAmount(source.getAmount()); + ingredient.setDescription(source.getDescription()); + ingredient.setUnitOfMeasure(uomConverter.convert(source.getUnitOfMeasure())); + return ingredient; + } +} diff --git a/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java new file mode 100644 index 0000000000..ceddd0c2c3 --- /dev/null +++ b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java @@ -0,0 +1,33 @@ +package guru.springframework.converters; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.domain.Ingredient; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class IngredientToIngredientCommand implements Converter { + private final UnitOfMeasureToUnitOfMeasureCommand uomConverter; + + public IngredientToIngredientCommand(UnitOfMeasureToUnitOfMeasureCommand uomConverter){ + this.uomConverter = uomConverter; + } + + @Synchronized + @Nullable + @Override + public IngredientCommand convert(Ingredient ingredient){ + if(ingredient == null){ + return null; + } + + IngredientCommand ingredientCommand = new IngredientCommand(); + ingredientCommand.setId(ingredient.getId()); + ingredientCommand.setAmount(ingredient.getAmount()); + ingredientCommand.setDescription(ingredient.getDescription()); + ingredientCommand.setUnitOfMeasure(uomConverter.convert(ingredient.getUnitOfMeasure())); + return ingredientCommand; + } +} diff --git a/src/main/java/guru/springframework/converters/NotesCommandToNotes.java b/src/main/java/guru/springframework/converters/NotesCommandToNotes.java new file mode 100644 index 0000000000..fbe833a1f5 --- /dev/null +++ b/src/main/java/guru/springframework/converters/NotesCommandToNotes.java @@ -0,0 +1,25 @@ +package guru.springframework.converters; + +import guru.springframework.commands.NotesCommand; +import guru.springframework.domain.Notes; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class NotesCommandToNotes implements Converter { + @Synchronized + @Nullable + @Override + public Notes convert(NotesCommand source){ + if(source == null){ + return null; + } + + final Notes notes = new Notes(); + notes.setId(source.getId()); + notes.setRecipeNotes(source.getRecipeNotes()); + return notes; + } +} diff --git a/src/main/java/guru/springframework/converters/NotesToNotesCommand.java b/src/main/java/guru/springframework/converters/NotesToNotesCommand.java new file mode 100644 index 0000000000..baab40b963 --- /dev/null +++ b/src/main/java/guru/springframework/converters/NotesToNotesCommand.java @@ -0,0 +1,25 @@ +package guru.springframework.converters; + +import guru.springframework.commands.NotesCommand; +import guru.springframework.domain.Notes; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class NotesToNotesCommand implements Converter { + @Synchronized + @Nullable + @Override + public NotesCommand convert(Notes source){ + if(source == null){ + return null; + } + + final NotesCommand notesCommand = new NotesCommand(); + notesCommand.setId(source.getId()); + notesCommand.setRecipeNotes(source.getRecipeNotes()); + return notesCommand; + } +} diff --git a/src/main/java/guru/springframework/converters/RecipeCommandToRecipe.java b/src/main/java/guru/springframework/converters/RecipeCommandToRecipe.java new file mode 100644 index 0000000000..f7c3e2c7bc --- /dev/null +++ b/src/main/java/guru/springframework/converters/RecipeCommandToRecipe.java @@ -0,0 +1,52 @@ +package guru.springframework.converters; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.domain.Recipe; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class RecipeCommandToRecipe implements Converter { + private final CategoryCommandToCategory categoryConverter; + private final IngredientCommandToIngredient ingredientConverter; + private final NotesCommandToNotes notesConverter; + + public RecipeCommandToRecipe(CategoryCommandToCategory categoryConverter, IngredientCommandToIngredient ingredientConverter, NotesCommandToNotes notesConverter) { + this.categoryConverter = categoryConverter; + this.ingredientConverter = ingredientConverter; + this.notesConverter = notesConverter; + } + + @Synchronized + @Nullable + @Override + public Recipe convert(RecipeCommand source){ + if(source == null){ + return null; + } + + final Recipe recipe = new Recipe(); + recipe.setId(source.getId()); + recipe.setCookTime(source.getCookTime()); + recipe.setPrepTime(source.getPrepTime()); + recipe.setDescription(source.getDescription()); + recipe.setDifficulty(source.getDifficulty()); + recipe.setDirections(source.getDirections()); + recipe.setServings(source.getServings()); + recipe.setSource(source.getSource()); + recipe.setUrl(source.getUrl()); + recipe.setNotes(notesConverter.convert(source.getNotes())); + + if(source.getCategories() != null && source.getCategories().size() > 0){ + source.getCategories().forEach(category -> recipe.getCategories().add(categoryConverter.convert(category))); + } + + if(source.getIngredients() != null && source.getIngredients().size() > 0){ + source.getIngredients().forEach(ingredient -> recipe.getIngredients().add(ingredientConverter.convert(ingredient))); + } + + return recipe; + } +} diff --git a/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java b/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java new file mode 100644 index 0000000000..ff85c5293e --- /dev/null +++ b/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java @@ -0,0 +1,53 @@ +package guru.springframework.converters; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.domain.Category; +import guru.springframework.domain.Recipe; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class RecipeToRecipeCommand implements Converter { + private final CategoryToCategoryCommand categoryConverter; + private final IngredientToIngredientCommand ingredientConverter; + private final NotesToNotesCommand notesConverter; + + public RecipeToRecipeCommand(CategoryToCategoryCommand categoryConverter, IngredientToIngredientCommand ingredientConverter, NotesToNotesCommand notesConverter) { + this.categoryConverter = categoryConverter; + this.ingredientConverter = ingredientConverter; + this.notesConverter = notesConverter; + } + + @Synchronized + @Nullable + @Override + public RecipeCommand convert(Recipe source){ + if(source == null){ + return null; + } + + final RecipeCommand command = new RecipeCommand(); + command.setId(source.getId()); + command.setCookTime(source.getCookTime()); + command.setPrepTime(source.getPrepTime()); + command.setDescription(source.getDescription()); + command.setDifficulty(source.getDifficulty()); + command.setDirections(source.getDirections()); + command.setServings(source.getServings()); + command.setSource(source.getSource()); + command.setUrl(source.getUrl()); + command.setNotes(notesConverter.convert(source.getNotes())); + + if(source.getCategories() != null && source.getCategories().size() > 0){ + source.getCategories().forEach((Category category) -> command.getCategories().add(categoryConverter.convert(category))); + } + + if(source.getIngredients() != null && source.getIngredients().size() > 0){ + source.getIngredients().forEach(ingredient -> command.getIngredients().add(ingredientConverter.convert(ingredient))); + } + + return command; + } +} diff --git a/src/main/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasure.java b/src/main/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasure.java new file mode 100644 index 0000000000..1f3be7d08a --- /dev/null +++ b/src/main/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasure.java @@ -0,0 +1,24 @@ +package guru.springframework.converters; + +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.domain.UnitOfMeasure; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class UnitOfMeasureCommandToUnitOfMeasure implements Converter { + @Synchronized + @Nullable + @Override + public UnitOfMeasure convert(UnitOfMeasureCommand source) { + if (source == null) { + return null; + } + final UnitOfMeasure uom = new UnitOfMeasure(); + uom.setId(source.getId()); + uom.setDescription(source.getDescription()); + return uom; + } +} diff --git a/src/main/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommand.java b/src/main/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommand.java new file mode 100644 index 0000000000..e765fe7fd0 --- /dev/null +++ b/src/main/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommand.java @@ -0,0 +1,24 @@ +package guru.springframework.converters; + +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.domain.UnitOfMeasure; +import lombok.Synchronized; +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +public class UnitOfMeasureToUnitOfMeasureCommand implements Converter { + @Synchronized + @Nullable + @Override + public UnitOfMeasureCommand convert(UnitOfMeasure unitOfMeasure){ + if(unitOfMeasure != null){ + final UnitOfMeasureCommand uomc = new UnitOfMeasureCommand(); + uomc.setId(unitOfMeasure.getId()); + uomc.setDescription(unitOfMeasure.getDescription()); + return uomc; + } + return null; + } +} diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index 2342262b4a..17ed3a5e38 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -37,8 +37,10 @@ public class Recipe { inverseJoinColumns = @JoinColumn(name = "category_id")) private Set categories = new HashSet<>(); public void setNotes(Notes notes) { - this.notes = notes; - notes.setRecipe(this); + if(notes != null) { + this.notes = notes; + notes.setRecipe(this); + } } public Recipe addIngredient(Ingredient ingredient){ ingredient.setRecipe(this); diff --git a/src/main/java/guru/springframework/services/RecipeService.java b/src/main/java/guru/springframework/services/RecipeService.java index b6cf997a1b..7c0d0288ce 100644 --- a/src/main/java/guru/springframework/services/RecipeService.java +++ b/src/main/java/guru/springframework/services/RecipeService.java @@ -1,5 +1,6 @@ package guru.springframework.services; +import guru.springframework.commands.RecipeCommand; import guru.springframework.domain.Recipe; import org.springframework.stereotype.Service; @@ -8,4 +9,5 @@ public interface RecipeService { Set getRecipes(); Recipe findById(Long l); + RecipeCommand saveRecipeCommand(RecipeCommand command); } diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index 35a228c51e..732bf28465 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -1,11 +1,14 @@ package guru.springframework.services; +import guru.springframework.commands.RecipeCommand; +import guru.springframework.converters.RecipeCommandToRecipe; +import guru.springframework.converters.RecipeToRecipeCommand; import guru.springframework.domain.Recipe; import guru.springframework.repositories.RecipeRepository; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import javax.transaction.Transactional; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -13,9 +16,13 @@ @Service public class RecipeServiceImpl implements RecipeService{ private final RecipeRepository recipeRepository; + private final RecipeCommandToRecipe recipeCommandToRecipe; + private final RecipeToRecipeCommand recipeToRecipeCommand; - public RecipeServiceImpl(RecipeRepository recipeRepository) { + public RecipeServiceImpl(RecipeRepository recipeRepository, RecipeCommandToRecipe recipeCommandToRecipe, RecipeToRecipeCommand recipeToRecipeCommand) { this.recipeRepository = recipeRepository; + this.recipeCommandToRecipe = recipeCommandToRecipe; + this.recipeToRecipeCommand = recipeToRecipeCommand; } @Override @@ -37,4 +44,13 @@ public Recipe findById(Long l){ return recipeOptional.get(); } + + @Override + @Transactional + public RecipeCommand saveRecipeCommand(RecipeCommand command){ + Recipe detachedRecipe = recipeCommandToRecipe.convert(command); + Recipe savedRecipe = recipeRepository.save(detachedRecipe); + log.debug("Saved recipeId: "+savedRecipe.getId()); + return recipeToRecipeCommand.convert(savedRecipe); + } } diff --git a/src/test/java/guru/springframework/converters/CategoryCommandToCategoryTest.java b/src/test/java/guru/springframework/converters/CategoryCommandToCategoryTest.java new file mode 100644 index 0000000000..fb16d689f8 --- /dev/null +++ b/src/test/java/guru/springframework/converters/CategoryCommandToCategoryTest.java @@ -0,0 +1,41 @@ +package guru.springframework.converters; + +import guru.springframework.commands.CategoryCommand; +import guru.springframework.domain.Category; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CategoryCommandToCategoryTest { + public static final Long idValue = 1L; + public static final String description = "description"; + CategoryCommandToCategory converter; + + @Before + public void setUp() throws Exception { + converter = new CategoryCommandToCategory(); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new CategoryCommand())); + } + + @Test + public void convert() throws Exception { + CategoryCommand categoryCommand = new CategoryCommand(); + categoryCommand.setId(idValue); + categoryCommand.setDescription(description); + + Category category = converter.convert(categoryCommand); + + assertEquals(idValue,category.getId()); + assertEquals(description,category.getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/CategoryToCategoryCommandTest.java b/src/test/java/guru/springframework/converters/CategoryToCategoryCommandTest.java new file mode 100644 index 0000000000..b465e52633 --- /dev/null +++ b/src/test/java/guru/springframework/converters/CategoryToCategoryCommandTest.java @@ -0,0 +1,40 @@ +package guru.springframework.converters; + +import guru.springframework.commands.CategoryCommand; +import guru.springframework.domain.Category; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CategoryToCategoryCommandTest { + public static final Long idValue = 1L; + public static final String description = "description"; + CategoryToCategoryCommand converter; + @Before + public void setUp() throws Exception { + converter = new CategoryToCategoryCommand(); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new Category())); + } + + @Test + public void convert() throws Exception { + Category category = new Category(); + category.setId(idValue); + category.setDescription(description); + + CategoryCommand categoryCommand = converter.convert(category); + + assertEquals(idValue,categoryCommand.getId()); + assertEquals(description,categoryCommand.getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java b/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java new file mode 100644 index 0000000000..d43fd2e9f5 --- /dev/null +++ b/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java @@ -0,0 +1,73 @@ +package guru.springframework.converters; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.domain.Ingredient; +import guru.springframework.domain.Recipe; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigDecimal; + +import static org.junit.Assert.*; + +public class IngredientCommandToIngredientTest { + public static final Recipe recipe = new Recipe(); + public static final BigDecimal amount = new BigDecimal("1"); + public static final String description = "Cheeseburguer"; + public static final Long idValue = 1L; + public static final Long uomId = 2L; + IngredientCommandToIngredient converter; + + @Before + public void setUp() throws Exception { + converter = new IngredientCommandToIngredient(new UnitOfMeasureCommandToUnitOfMeasure()); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new IngredientCommand())); + } + + @Test + public void convert() throws Exception { + IngredientCommand command = new IngredientCommand(); + command.setId(idValue); + command.setAmount(amount); + command.setDescription(description); + UnitOfMeasureCommand unitOfMeasureCommand = new UnitOfMeasureCommand(); + unitOfMeasureCommand.setId(uomId); + command.setUnitOfMeasure(unitOfMeasureCommand); + + Ingredient ingredient = converter.convert(command); + + assertNotNull(ingredient); + assertNotNull(ingredient.getUnitOfMeasure()); + assertEquals(idValue,ingredient.getId()); + assertEquals(amount,ingredient.getAmount()); + assertEquals(description,ingredient.getDescription()); + assertEquals(uomId,ingredient.getUnitOfMeasure().getId()); + } + + @Test + public void convertWithNullUOM() throws Exception { + IngredientCommand command = new IngredientCommand(); + command.setId(idValue); + command.setAmount(amount); + command.setDescription(description); + UnitOfMeasureCommand unitOfMeasureCommand = new UnitOfMeasureCommand(); + + Ingredient ingredient = converter.convert(command); + + assertNotNull(ingredient); + assertNull(ingredient.getUnitOfMeasure()); + assertEquals(idValue,ingredient.getId()); + assertEquals(amount,ingredient.getAmount()); + assertEquals(description,ingredient.getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java b/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java new file mode 100644 index 0000000000..334c7026c5 --- /dev/null +++ b/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java @@ -0,0 +1,71 @@ +package guru.springframework.converters; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.domain.Ingredient; +import guru.springframework.domain.Recipe; +import guru.springframework.domain.UnitOfMeasure; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigDecimal; + +import static org.junit.Assert.*; + +public class IngredientToIngredientCommandTest { + public static final Recipe recipe = new Recipe(); + public static final BigDecimal amount = new BigDecimal("1"); + public static final String description = "description"; + public static final Long uomID = 2L; + public static final Long idValue = 1L; + IngredientToIngredientCommand converter; + + @Before + public void setUp() throws Exception { + converter = new IngredientToIngredientCommand(new UnitOfMeasureToUnitOfMeasureCommand()); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new Ingredient())); + } + + @Test + public void testConvertWithUOM() throws Exception { + Ingredient ingredient = new Ingredient(); + ingredient.setId(idValue); + ingredient.setAmount(amount); + ingredient.setDescription(description); + UnitOfMeasure uom = new UnitOfMeasure(); + uom.setId(uomID); + ingredient.setUnitOfMeasure(uom); + + IngredientCommand ingredientCommand = converter.convert(ingredient); + + assertEquals(idValue,ingredientCommand.getId()); + assertNotNull(ingredientCommand.getUnitOfMeasure()); + assertEquals(uomID,ingredientCommand.getUnitOfMeasure().getId()); + assertEquals(amount,ingredientCommand.getAmount()); + assertEquals(description,ingredientCommand.getDescription()); + } + + @Test + public void testConvertNullUOM() throws Exception { + Ingredient ingredient = new Ingredient(); + ingredient.setId(idValue); + ingredient.setAmount(amount); + ingredient.setDescription(description); + ingredient.setUnitOfMeasure(null); + + IngredientCommand ingredientCommand = converter.convert(ingredient); + + assertEquals(idValue,ingredientCommand.getId()); + assertNull(ingredientCommand.getUnitOfMeasure()); + assertEquals(amount,ingredientCommand.getAmount()); + assertEquals(description,ingredientCommand.getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/NotesCommandToNotesTest.java b/src/test/java/guru/springframework/converters/NotesCommandToNotesTest.java new file mode 100644 index 0000000000..9b998f64eb --- /dev/null +++ b/src/test/java/guru/springframework/converters/NotesCommandToNotesTest.java @@ -0,0 +1,41 @@ +package guru.springframework.converters; + +import guru.springframework.commands.NotesCommand; +import guru.springframework.domain.Notes; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class NotesCommandToNotesTest { + private static final Long idValue = 1L; + public static final String recipeNotes = "Notes"; + NotesCommandToNotes converter; + @Before + public void setUp() throws Exception { + converter = new NotesCommandToNotes(); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new NotesCommand())); + } + + @Test + public void convert() { + NotesCommand notesCommand = new NotesCommand(); + notesCommand.setId(idValue); + notesCommand.setRecipeNotes(recipeNotes); + + Notes notes = converter.convert(notesCommand); + + assertNotNull(notes); + assertEquals(idValue,notes.getId()); + assertEquals(recipeNotes,notes.getRecipeNotes()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/NotesToNotesCommandTest.java b/src/test/java/guru/springframework/converters/NotesToNotesCommandTest.java new file mode 100644 index 0000000000..6676f96d47 --- /dev/null +++ b/src/test/java/guru/springframework/converters/NotesToNotesCommandTest.java @@ -0,0 +1,40 @@ +package guru.springframework.converters; + +import guru.springframework.commands.NotesCommand; +import guru.springframework.domain.Notes; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class NotesToNotesCommandTest { + private static final Long idValue = 1L; + private static final String recipeNotes = "Notes"; + NotesToNotesCommand converter; + @Before + public void setUp() throws Exception { + converter = new NotesToNotesCommand(); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new Notes())); + } + + @Test + public void convert() throws Exception { + Notes notes = new Notes(); + notes.setId(idValue); + notes.setRecipeNotes(recipeNotes); + + NotesCommand notesCommand = converter.convert(notes); + + assertEquals(idValue,notesCommand.getId()); + assertEquals(recipeNotes,notesCommand.getRecipeNotes()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/RecipeCommandToRecipeTest.java b/src/test/java/guru/springframework/converters/RecipeCommandToRecipeTest.java new file mode 100644 index 0000000000..773a60f032 --- /dev/null +++ b/src/test/java/guru/springframework/converters/RecipeCommandToRecipeTest.java @@ -0,0 +1,97 @@ +package guru.springframework.converters; + +import guru.springframework.commands.CategoryCommand; +import guru.springframework.commands.IngredientCommand; +import guru.springframework.commands.NotesCommand; +import guru.springframework.commands.RecipeCommand; +import guru.springframework.domain.Difficulty; +import guru.springframework.domain.Recipe; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class RecipeCommandToRecipeTest { + public static final Long recipeid = 1L; + public static final Integer cookTime = Integer.valueOf("5"); + public static final Integer prepTime = Integer.valueOf("7"); + public static final String description = "My Recipe"; + public static final String directions = "Directions"; + public static final Difficulty difficulty = Difficulty.EASY; + public static final Integer servings = Integer.valueOf("3"); + public static final String source = "Source"; + public static final String url = "Some url"; + public static final Long catid1 = 1L; + public static final Long catid2 = 2L; + public static final Long ingred1 = 3L; + public static final Long ingred2 = 4L; + public static final Long notesId = 9L; + RecipeCommandToRecipe converter; + @Before + public void setUp() throws Exception { + converter = new RecipeCommandToRecipe(new CategoryCommandToCategory(),new IngredientCommandToIngredient(new UnitOfMeasureCommandToUnitOfMeasure()),new NotesCommandToNotes()); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new RecipeCommand())); + } + + @Test + public void convert() throws Exception{ + RecipeCommand recipeCommand = new RecipeCommand(); + recipeCommand.setId(recipeid); + recipeCommand.setCookTime(cookTime); + recipeCommand.setPrepTime(prepTime); + recipeCommand.setDescription(description); + recipeCommand.setDirections(directions); + recipeCommand.setDifficulty(difficulty); + recipeCommand.setServings(servings); + recipeCommand.setSource(source); + recipeCommand.setUrl(url); + + NotesCommand notes = new NotesCommand(); + notes.setId(notesId); + recipeCommand.setNotes(notes); + + CategoryCommand category = new CategoryCommand(); + category.setId(catid1); + + CategoryCommand category2 = new CategoryCommand(); + category2.setId(catid2); + + IngredientCommand ingredient = new IngredientCommand(); + ingredient.setId(ingred1); + + IngredientCommand ingredient2 = new IngredientCommand(); + ingredient2.setId(ingred2); + + recipeCommand.getCategories().add(category); + recipeCommand.getCategories().add(category2); + + recipeCommand.getIngredients().add(ingredient); + recipeCommand.getIngredients().add(ingredient2); + + Recipe recipe = converter.convert(recipeCommand); + + assertNotNull(recipe); + assertEquals(recipeid,recipe.getId()); + assertEquals(cookTime,recipe.getCookTime()); + assertEquals(prepTime,recipe.getPrepTime()); + assertEquals(description,recipe.getDescription()); + assertEquals(directions,recipe.getDirections()); + assertEquals(difficulty,recipe.getDifficulty()); + assertEquals(servings,recipe.getServings()); + assertEquals(source,recipe.getSource()); + assertEquals(url,recipe.getUrl()); + assertEquals(notesId,recipe.getNotes().getId()); + assertEquals(2,recipe.getCategories().size()); + assertEquals(2,recipe.getIngredients().size()); + + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/RecipeToRecipeCommandTest.java b/src/test/java/guru/springframework/converters/RecipeToRecipeCommandTest.java new file mode 100644 index 0000000000..9df99ce065 --- /dev/null +++ b/src/test/java/guru/springframework/converters/RecipeToRecipeCommandTest.java @@ -0,0 +1,93 @@ +package guru.springframework.converters; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.domain.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class RecipeToRecipeCommandTest { + public static final Long recipeid = 1L; + public static final Integer cookTime = Integer.valueOf("5"); + public static final Integer prepTime = Integer.valueOf("7"); + public static final String description = "My Recipe"; + public static final String directions = "Directions"; + public static final Difficulty difficulty = Difficulty.EASY; + public static final Integer servings = Integer.valueOf("3"); + public static final String source = "Source"; + public static final String url = "Some url"; + public static final Long catId1 = 1L; + public static final Long catId2 = 2L; + public static final Long ingred1 = 3L; + public static final Long ingred2 = 4L; + public static final Long notesId = 9L; + RecipeToRecipeCommand converter; + + @Before + public void setUp() throws Exception { + converter = new RecipeToRecipeCommand(new CategoryToCategoryCommand(),new IngredientToIngredientCommand(new UnitOfMeasureToUnitOfMeasureCommand()),new NotesToNotesCommand()); + } + + @Test + public void testNullObject() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception{ + assertNotNull(converter.convert(new Recipe())); + } + + @Test + public void convert() throws Exception{ + Recipe recipe = new Recipe(); + recipe.setId(recipeid); + recipe.setCookTime(cookTime); + recipe.setPrepTime(prepTime); + recipe.setDescription(description); + recipe.setDirections(directions); + recipe.setDifficulty(difficulty); + recipe.setServings(servings); + recipe.setSource(source); + recipe.setUrl(url); + + Notes notes = new Notes(); + notes.setId(notesId); + recipe.setNotes(notes); + + Category category = new Category(); + category.setId(catId1); + + Category category2 = new Category(); + category2.setId(catId2); + + Ingredient ingredient = new Ingredient(); + ingredient.setId(ingred1); + + Ingredient ingredient2 = new Ingredient(); + ingredient2.setId(ingred2); + + recipe.getCategories().add(category); + recipe.getCategories().add(category2); + + recipe.getIngredients().add(ingredient); + recipe.getIngredients().add(ingredient2); + + RecipeCommand command = converter.convert(recipe); + + assertNotNull(command); + assertEquals(recipeid,command.getId()); + assertEquals(cookTime,command.getCookTime()); + assertEquals(prepTime, command.getPrepTime()); + assertEquals(description,command.getDescription()); + assertEquals(difficulty,command.getDifficulty()); + assertEquals(directions,command.getDirections()); + assertEquals(servings,command.getServings()); + assertEquals(source,command.getSource()); + assertEquals(url,command.getUrl()); + assertEquals(notesId,command.getNotes().getId()); + assertEquals(2,command.getCategories().size()); + assertEquals(2,command.getIngredients().size()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasureTest.java b/src/test/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasureTest.java new file mode 100644 index 0000000000..b351170f9c --- /dev/null +++ b/src/test/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasureTest.java @@ -0,0 +1,41 @@ +package guru.springframework.converters; + +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.domain.UnitOfMeasure; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class UnitOfMeasureCommandToUnitOfMeasureTest { + private static final String description = "Description"; + private static final Long longVal = 1L; + UnitOfMeasureCommandToUnitOfMeasure converter; + + @Before + public void setUp() throws Exception { + converter = new UnitOfMeasureCommandToUnitOfMeasure(); + } + + @Test + public void testNullParameter() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new UnitOfMeasureCommand())); + } + + @Test + public void convert() throws Exception { + UnitOfMeasureCommand command = new UnitOfMeasureCommand(); + command.setId(longVal); + command.setDescription(description); + + UnitOfMeasure uom = converter.convert(command); + assertNotNull(uom); + assertEquals(longVal,uom.getId()); + assertEquals(description,uom.getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommandTest.java b/src/test/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommandTest.java new file mode 100644 index 0000000000..ce7a1546a3 --- /dev/null +++ b/src/test/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommandTest.java @@ -0,0 +1,39 @@ +package guru.springframework.converters; + +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.domain.UnitOfMeasure; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class UnitOfMeasureToUnitOfMeasureCommandTest { + public static final Long idValue = 1L; + public static final String description = "description"; + UnitOfMeasureToUnitOfMeasureCommand converter; + @Before + public void setUp() throws Exception { + converter = new UnitOfMeasureToUnitOfMeasureCommand(); + } + + @Test + public void testNullParameter() throws Exception { + assertNull(converter.convert(null)); + } + + @Test + public void testEmptyObject() throws Exception { + assertNotNull(converter.convert(new UnitOfMeasure())); + } + + @Test + public void convert() { + UnitOfMeasure uom = new UnitOfMeasure(); + uom.setId(idValue); + uom.setDescription(description); + + UnitOfMeasureCommand command = converter.convert(uom); + assertEquals(idValue,command.getId()); + assertEquals(description,command.getDescription()); + } +} \ No newline at end of file diff --git a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java index e9ca2e3bb7..31bbccffd1 100644 --- a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java +++ b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java @@ -1,5 +1,7 @@ package guru.springframework.services; +import guru.springframework.converters.RecipeCommandToRecipe; +import guru.springframework.converters.RecipeToRecipeCommand; import guru.springframework.domain.Recipe; import guru.springframework.repositories.RecipeRepository; import org.junit.Before; @@ -19,11 +21,17 @@ public class RecipeServiceImplTest { @Mock RecipeRepository recipeRepository; + @Mock + RecipeToRecipeCommand recipeToRecipeCommand; + + @Mock + RecipeCommandToRecipe recipeCommandToRecipe; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - recipeService = new RecipeServiceImpl(recipeRepository); + recipeService = new RecipeServiceImpl(recipeRepository,recipeCommandToRecipe, recipeToRecipeCommand); } @Test diff --git a/src/test/java/guru/springframework/services/RecipeServiceTest.java b/src/test/java/guru/springframework/services/RecipeServiceTest.java new file mode 100644 index 0000000000..df47c35f1a --- /dev/null +++ b/src/test/java/guru/springframework/services/RecipeServiceTest.java @@ -0,0 +1,57 @@ +package guru.springframework.services; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.converters.RecipeCommandToRecipe; +import guru.springframework.converters.RecipeToRecipeCommand; +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; + +import static org.junit.Assert.assertEquals; + + +/** + * Created by jt on 6/21/17. + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class RecipeServiceTest{ + + public static final String NEW_DESCRIPTION = "New Description"; + + @Autowired + RecipeService recipeService; + + @Autowired + RecipeRepository recipeRepository; + + @Autowired + RecipeCommandToRecipe recipeCommandToRecipe; + + @Autowired + RecipeToRecipeCommand recipeToRecipeCommand; + + @Transactional + @Test + public void testSaveOfDescription() throws Exception { + //given + Iterable recipes = recipeRepository.findAll(); + Recipe testRecipe = recipes.iterator().next(); + RecipeCommand testRecipeCommand = recipeToRecipeCommand.convert(testRecipe); + + //when + testRecipeCommand.setDescription(NEW_DESCRIPTION); + RecipeCommand savedRecipeCommand = recipeService.saveRecipeCommand(testRecipeCommand); + + //then + assertEquals(NEW_DESCRIPTION, savedRecipeCommand.getDescription()); + assertEquals(testRecipe.getId(), savedRecipeCommand.getId()); + assertEquals(testRecipe.getCategories().size(), savedRecipeCommand.getCategories().size()); + assertEquals(testRecipe.getIngredients().size(), savedRecipeCommand.getIngredients().size()); + } +} \ No newline at end of file From b9b2d003c186ccc12084e78f624e7c93e3ab0c38 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 4 Oct 2023 10:27:47 -0600 Subject: [PATCH 13/36] Adding a New Recipe --- .../controllers/RecipeController.java | 17 +- .../templates/recipe/recipeform.html | 151 ++++++++++++++++++ src/main/resources/templates/recipe/show.html | 1 - 3 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/templates/recipe/recipeform.html diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index d16de4a93c..8df1c8c33f 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -1,10 +1,10 @@ package guru.springframework.controllers; +import guru.springframework.commands.RecipeCommand; import guru.springframework.services.RecipeService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; @Controller public class RecipeController { @@ -18,4 +18,17 @@ public String showById(@PathVariable String id, Model model){ model.addAttribute("recipe",recipeService.findById(Long.valueOf(id))); return "recipe/show"; } + @RequestMapping("recipe/new") + public String newRecipe(Model model){ + model.addAttribute("recipe", new RecipeCommand()); + + return "recipe/recipeform"; + } + @PostMapping + @RequestMapping("recipe") + public String serveOrUpdate(@ModelAttribute RecipeCommand command){ + RecipeCommand savedCommand = recipeService.saveRecipeCommand(command); + + return "redirect:/recipe/show/" + savedCommand.getId(); + } } diff --git a/src/main/resources/templates/recipe/recipeform.html b/src/main/resources/templates/recipe/recipeform.html new file mode 100644 index 0000000000..7e1b794f1d --- /dev/null +++ b/src/main/resources/templates/recipe/recipeform.html @@ -0,0 +1,151 @@ + + + + + Show Recipe + + + + + + + + + +
+
+
+
+ +
+
+
+

Edit Recipe Information

+
+
+
+
+ + +
+
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+
+

Ingredients

+
+
+ Edit +
+
+
+
+
+
+
    +
  • 1 Cup of milk
  • +
  • 1 Teaspoon of chocolate
  • +
  • 1 Teaspoon of Sugar +
  • +
+
+
+
+
+
+
+

Directions

+
+
+
+
+
+
+
+
+
+
+

Notes

+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + + diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html index eb1415f7b3..080a5c2f4c 100644 --- a/src/main/resources/templates/recipe/show.html +++ b/src/main/resources/templates/recipe/show.html @@ -21,7 +21,6 @@
- Recipe Description Here!
From c2e4f3ddfee43fba8a0b348975cb2728957f8aad Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 4 Oct 2023 11:00:24 -0600 Subject: [PATCH 14/36] Updating a Recipe --- .../commands/IngredientCommand.java | 2 +- .../controllers/RecipeController.java | 10 ++++- .../IngredientCommandToIngredient.java | 2 +- .../IngredientToIngredientCommand.java | 2 +- .../services/RecipeService.java | 1 + .../services/RecipeServiceImpl.java | 6 ++- src/main/resources/templates/index.html | 7 ++- .../controllers/RecipeControllerTest.java | 45 +++++++++++++++++-- 8 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/main/java/guru/springframework/commands/IngredientCommand.java b/src/main/java/guru/springframework/commands/IngredientCommand.java index 85418c951a..ee9b04c670 100644 --- a/src/main/java/guru/springframework/commands/IngredientCommand.java +++ b/src/main/java/guru/springframework/commands/IngredientCommand.java @@ -13,5 +13,5 @@ public class IngredientCommand { private Long id; private String description; private BigDecimal amount; - private UnitOfMeasureCommand unitOfMeasure; + private UnitOfMeasureCommand uom; } diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index 8df1c8c33f..247877abb7 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -13,7 +13,7 @@ public class RecipeController { public RecipeController(RecipeService recipeService) { this.recipeService = recipeService; } - @RequestMapping("/recipe/show/{id}") + @RequestMapping("/recipe/{id}/show") public String showById(@PathVariable String id, Model model){ model.addAttribute("recipe",recipeService.findById(Long.valueOf(id))); return "recipe/show"; @@ -24,11 +24,17 @@ public String newRecipe(Model model){ return "recipe/recipeform"; } + + @RequestMapping("recipe/{id}/update") + public String updateRecipe(@PathVariable String id, Model model){ + model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(id))); + return "recipe/recipeform"; + } @PostMapping @RequestMapping("recipe") public String serveOrUpdate(@ModelAttribute RecipeCommand command){ RecipeCommand savedCommand = recipeService.saveRecipeCommand(command); - return "redirect:/recipe/show/" + savedCommand.getId(); + return "redirect:/recipe/" + savedCommand.getId() + "/show"; } } diff --git a/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java b/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java index 53ff6b3028..852c878b63 100644 --- a/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java +++ b/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java @@ -25,7 +25,7 @@ public Ingredient convert(IngredientCommand source){ ingredient.setId(source.getId()); ingredient.setAmount(source.getAmount()); ingredient.setDescription(source.getDescription()); - ingredient.setUnitOfMeasure(uomConverter.convert(source.getUnitOfMeasure())); + ingredient.setUnitOfMeasure(uomConverter.convert(source.getUom())); return ingredient; } } diff --git a/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java index ceddd0c2c3..599989ac5b 100644 --- a/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java +++ b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java @@ -27,7 +27,7 @@ public IngredientCommand convert(Ingredient ingredient){ ingredientCommand.setId(ingredient.getId()); ingredientCommand.setAmount(ingredient.getAmount()); ingredientCommand.setDescription(ingredient.getDescription()); - ingredientCommand.setUnitOfMeasure(uomConverter.convert(ingredient.getUnitOfMeasure())); + ingredientCommand.setUom(uomConverter.convert(ingredient.getUnitOfMeasure())); return ingredientCommand; } } diff --git a/src/main/java/guru/springframework/services/RecipeService.java b/src/main/java/guru/springframework/services/RecipeService.java index 7c0d0288ce..fae5a15fab 100644 --- a/src/main/java/guru/springframework/services/RecipeService.java +++ b/src/main/java/guru/springframework/services/RecipeService.java @@ -9,5 +9,6 @@ public interface RecipeService { Set getRecipes(); Recipe findById(Long l); + RecipeCommand findCommandById(Long l); RecipeCommand saveRecipeCommand(RecipeCommand command); } diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index 732bf28465..0f7703167c 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -44,7 +44,11 @@ public Recipe findById(Long l){ return recipeOptional.get(); } - + @Override + @Transactional + public RecipeCommand findCommandById(Long l){ + return recipeToRecipeCommand.convert(findById(l)); + } @Override @Transactional public RecipeCommand saveRecipeCommand(RecipeCommand command){ diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index b2f4bf8d93..5090ccbd42 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -16,7 +16,9 @@ th:href="@{/webjars/bootstrap/5.2.3/js/bootstrap.min.js}"> -
+ + +
@@ -47,7 +49,8 @@

My Recipes!

678 Sweet - View + View + Update
diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index 4efba9a522..d2ac30cba3 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -1,17 +1,20 @@ package guru.springframework.controllers; +import guru.springframework.commands.RecipeCommand; import guru.springframework.domain.Recipe; import guru.springframework.services.RecipeService; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; @@ -20,10 +23,13 @@ public class RecipeControllerTest { RecipeService recipeService; RecipeController recipeController; + MockMvc mockMvc; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); recipeController = new RecipeController(recipeService); + mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build(); } @Test @@ -31,13 +37,46 @@ public void testGetRecipe() throws Exception { Recipe recipe = new Recipe(); recipe.setId(1L); - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build(); - when(recipeService.findById(anyLong())).thenReturn(recipe); - mockMvc.perform(MockMvcRequestBuilders.get("/recipe/show/1")) + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/show")) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.view().name("recipe/show")) .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); } + + @Test + public void testGetNewRecipeForm() throws Exception { + RecipeCommand command = new RecipeCommand(); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/new")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/recipeform")) + .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + } + + @Test + public void testPostNewRecipeForm() throws Exception { + RecipeCommand command = new RecipeCommand(); + command.setId(2L); + + when(recipeService.saveRecipeCommand(any())).thenReturn(command); + + mockMvc.perform(MockMvcRequestBuilders.post("/recipe").contentType(MediaType.APPLICATION_FORM_URLENCODED).param("id","").param("description","some string")) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/show/2")); + } + + @Test + public void testGetUpdateView() throws Exception { + RecipeCommand command = new RecipeCommand(); + command.setId(2L); + + when(recipeService.findCommandById(anyLong())).thenReturn(command); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/update")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/recipeform")) + .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + } } \ No newline at end of file From bb8aedfabbfc8dd612cda3d1b9982930d07a3bfb Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 4 Oct 2023 11:21:30 -0600 Subject: [PATCH 15/36] Deleting a Recipe --- .../controllers/RecipeController.java | 15 ++++++++++++++- .../springframework/services/RecipeService.java | 1 + .../services/RecipeServiceImpl.java | 5 +++++ src/main/resources/application.properties | 1 + src/main/resources/templates/index.html | 7 +++++++ .../controllers/RecipeControllerTest.java | 13 +++++++++++-- .../IngredientCommandToIngredientTest.java | 2 +- .../IngredientToIngredientCommandTest.java | 6 +++--- .../services/RecipeServiceImplTest.java | 8 ++++++++ 9 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index 247877abb7..37e138e16a 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -2,10 +2,12 @@ import guru.springframework.commands.RecipeCommand; import guru.springframework.services.RecipeService; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +@Slf4j @Controller public class RecipeController { private final RecipeService recipeService; @@ -13,18 +15,20 @@ public class RecipeController { public RecipeController(RecipeService recipeService) { this.recipeService = recipeService; } + @GetMapping @RequestMapping("/recipe/{id}/show") public String showById(@PathVariable String id, Model model){ model.addAttribute("recipe",recipeService.findById(Long.valueOf(id))); return "recipe/show"; } + @GetMapping @RequestMapping("recipe/new") public String newRecipe(Model model){ model.addAttribute("recipe", new RecipeCommand()); return "recipe/recipeform"; } - + @GetMapping @RequestMapping("recipe/{id}/update") public String updateRecipe(@PathVariable String id, Model model){ model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(id))); @@ -37,4 +41,13 @@ public String serveOrUpdate(@ModelAttribute RecipeCommand command){ return "redirect:/recipe/" + savedCommand.getId() + "/show"; } + + @GetMapping + @RequestMapping("recipe/{id}/delete") + public String deleteById(@PathVariable String id){ + log.debug("Deleting id: " +id); + + recipeService.deleteById(Long.valueOf(id)); + return "redirect:/"; + } } diff --git a/src/main/java/guru/springframework/services/RecipeService.java b/src/main/java/guru/springframework/services/RecipeService.java index fae5a15fab..41c972b5a2 100644 --- a/src/main/java/guru/springframework/services/RecipeService.java +++ b/src/main/java/guru/springframework/services/RecipeService.java @@ -11,4 +11,5 @@ public interface RecipeService { Recipe findById(Long l); RecipeCommand findCommandById(Long l); RecipeCommand saveRecipeCommand(RecipeCommand command); + void deleteById(Long idToDelete); } diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index 0f7703167c..e999f92730 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -57,4 +57,9 @@ public RecipeCommand saveRecipeCommand(RecipeCommand command){ log.debug("Saved recipeId: "+savedRecipe.getId()); return recipeToRecipeCommand.convert(savedRecipe); } + + @Override + public void deleteById(Long idToDelete){ + recipeRepository.deleteById(idToDelete); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29bb2..e45c5013c5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +logging.level.guru.springframework=debug \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 5090ccbd42..8f2f6eae06 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -33,17 +33,23 @@

My Recipes!

ID Description View + Update + Delete 123 Tasty View + Update + Delete 345 Spicy View + Update + Delete @@ -51,6 +57,7 @@

My Recipes!

Sweet View Update + Delete
diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index d2ac30cba3..3bb912fe4b 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -16,7 +16,7 @@ import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class RecipeControllerTest { @Mock @@ -64,7 +64,7 @@ public void testPostNewRecipeForm() throws Exception { mockMvc.perform(MockMvcRequestBuilders.post("/recipe").contentType(MediaType.APPLICATION_FORM_URLENCODED).param("id","").param("description","some string")) .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) - .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/show/2")); + .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/2/show")); } @Test @@ -79,4 +79,13 @@ public void testGetUpdateView() throws Exception { .andExpect(MockMvcResultMatchers.view().name("recipe/recipeform")) .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); } + + @Test + public void testDeleteAction() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/delete")) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andExpect(MockMvcResultMatchers.view().name("redirect:/")); + + verify(recipeService,times(1)).deleteById(anyLong()); + } } \ No newline at end of file diff --git a/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java b/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java index d43fd2e9f5..9295f3e0dc 100644 --- a/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java +++ b/src/test/java/guru/springframework/converters/IngredientCommandToIngredientTest.java @@ -42,7 +42,7 @@ public void convert() throws Exception { command.setDescription(description); UnitOfMeasureCommand unitOfMeasureCommand = new UnitOfMeasureCommand(); unitOfMeasureCommand.setId(uomId); - command.setUnitOfMeasure(unitOfMeasureCommand); + command.setUom(unitOfMeasureCommand); Ingredient ingredient = converter.convert(command); diff --git a/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java b/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java index 334c7026c5..2c59ad0ac2 100644 --- a/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java +++ b/src/test/java/guru/springframework/converters/IngredientToIngredientCommandTest.java @@ -47,8 +47,8 @@ public void testConvertWithUOM() throws Exception { IngredientCommand ingredientCommand = converter.convert(ingredient); assertEquals(idValue,ingredientCommand.getId()); - assertNotNull(ingredientCommand.getUnitOfMeasure()); - assertEquals(uomID,ingredientCommand.getUnitOfMeasure().getId()); + assertNotNull(ingredientCommand.getUom()); + assertEquals(uomID,ingredientCommand.getUom().getId()); assertEquals(amount,ingredientCommand.getAmount()); assertEquals(description,ingredientCommand.getDescription()); } @@ -64,7 +64,7 @@ public void testConvertNullUOM() throws Exception { IngredientCommand ingredientCommand = converter.convert(ingredient); assertEquals(idValue,ingredientCommand.getId()); - assertNull(ingredientCommand.getUnitOfMeasure()); + assertNull(ingredientCommand.getUom()); assertEquals(amount,ingredientCommand.getAmount()); assertEquals(description,ingredientCommand.getDescription()); } diff --git a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java index 31bbccffd1..167719c876 100644 --- a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java +++ b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java @@ -62,4 +62,12 @@ public void getRecipesTest() throws Exception { verify(recipeRepository,times(1)).findAll(); verify(recipeRepository,never()).findById(anyLong()); } + + @Test + public void testDeleteById() throws Exception { + Long idToDelete = Long.valueOf(2L); + recipeService.deleteById(idToDelete); + + verify(recipeRepository,times(1)).deleteById(anyLong()); + } } \ No newline at end of file From dbf224ec656d335689c7792159860b736d8756ea Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 4 Oct 2023 12:18:37 -0600 Subject: [PATCH 16/36] View Ingredients --- .../controllers/IngredientController.java | 29 ++++++++ .../templates/recipe/ingredient/list.html | 68 +++++++++++++++++++ src/main/resources/templates/recipe/show.html | 11 ++- .../controllers/IngredientControllerTest.java | 42 ++++++++++++ 4 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/main/java/guru/springframework/controllers/IngredientController.java create mode 100644 src/main/resources/templates/recipe/ingredient/list.html create mode 100644 src/test/java/guru/springframework/controllers/IngredientControllerTest.java diff --git a/src/main/java/guru/springframework/controllers/IngredientController.java b/src/main/java/guru/springframework/controllers/IngredientController.java new file mode 100644 index 0000000000..c4196932cd --- /dev/null +++ b/src/main/java/guru/springframework/controllers/IngredientController.java @@ -0,0 +1,29 @@ +package guru.springframework.controllers; + +import guru.springframework.services.RecipeService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +@Slf4j +@Controller +public class IngredientController { + private final RecipeService recipeService; + + public IngredientController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + @GetMapping + @RequestMapping("/recipe/{recipeId}/ingredients") + public String listIngredients(@PathVariable String recipeId, Model model){ + log.debug("Getting ingredient list for recipe id: "+recipeId); + + model.addAttribute("recipe",recipeService.findCommandById(Long.valueOf(recipeId))); + + return "recipe/ingredient/list"; + } +} diff --git a/src/main/resources/templates/recipe/ingredient/list.html b/src/main/resources/templates/recipe/ingredient/list.html new file mode 100644 index 0000000000..ce65ac35cb --- /dev/null +++ b/src/main/resources/templates/recipe/ingredient/list.html @@ -0,0 +1,68 @@ + + + + + List Ingredients + + + + + + + +
+
+
+
+
+
+

Ingredients

+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDDescriptionViewUpdateDelete
123Tasty Goodnees 1ViewUpdateDelete
12333Tasty Goodnees 2ViewUpdateDelete
334Tasty Goodnees 3ViewUpdateDelete
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html index 080a5c2f4c..e17190a6f4 100644 --- a/src/main/resources/templates/recipe/show.html +++ b/src/main/resources/templates/recipe/show.html @@ -83,8 +83,15 @@
-
- Ingredients +
+
+
+

Ingredients

+
+
+ View +
+
diff --git a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java new file mode 100644 index 0000000000..fce28b634d --- /dev/null +++ b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java @@ -0,0 +1,42 @@ +package guru.springframework.controllers; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.services.RecipeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.*; + +public class IngredientControllerTest { + @Mock + RecipeService recipeService; + IngredientController controller; + MockMvc mockMvc; + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + controller = new IngredientController(recipeService); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + public void testListIngredients() throws Exception { + RecipeCommand recipeCommand = new RecipeCommand(); + when(recipeService.findCommandById(anyLong())).thenReturn(recipeCommand); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/ingredients")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/ingredient/list")) + .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + + verify(recipeService,times(1)).findCommandById(anyLong()); + } +} \ No newline at end of file From 1e31fae2c2e393b69179d3f956f89a7ba2f85cbf Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 1 Dec 2023 09:45:00 -0600 Subject: [PATCH 17/36] View and Update Ingredients --- .../commands/IngredientCommand.java | 1 + .../controllers/IngredientController.java | 38 ++++++- .../IngredientToIngredientCommand.java | 3 + .../services/IngredientService.java | 8 ++ .../services/IngredientServiceImpl.java | 85 ++++++++++++++ .../services/UnitOfMeasureService.java | 9 ++ .../services/UnitOfMeasureServiceImpl.java | 29 +++++ .../recipe/ingredient/ingredientform.html | 61 ++++++++++ .../templates/recipe/ingredient/show.html | 55 +++++++++ .../controllers/IngredientControllerTest.java | 57 +++++++++- .../services/IngredientServiceImplTest.java | 105 ++++++++++++++++++ .../UnitOfMeasureServiceImplTest.java | 47 ++++++++ 12 files changed, 493 insertions(+), 5 deletions(-) create mode 100644 src/main/java/guru/springframework/services/IngredientService.java create mode 100644 src/main/java/guru/springframework/services/IngredientServiceImpl.java create mode 100644 src/main/java/guru/springframework/services/UnitOfMeasureService.java create mode 100644 src/main/java/guru/springframework/services/UnitOfMeasureServiceImpl.java create mode 100644 src/main/resources/templates/recipe/ingredient/ingredientform.html create mode 100644 src/main/resources/templates/recipe/ingredient/show.html create mode 100644 src/test/java/guru/springframework/services/IngredientServiceImplTest.java create mode 100644 src/test/java/guru/springframework/services/UnitOfMeasureServiceImplTest.java diff --git a/src/main/java/guru/springframework/commands/IngredientCommand.java b/src/main/java/guru/springframework/commands/IngredientCommand.java index ee9b04c670..4ff0855636 100644 --- a/src/main/java/guru/springframework/commands/IngredientCommand.java +++ b/src/main/java/guru/springframework/commands/IngredientCommand.java @@ -11,6 +11,7 @@ @NoArgsConstructor public class IngredientCommand { private Long id; + private Long recipeId; private String description; private BigDecimal amount; private UnitOfMeasureCommand uom; diff --git a/src/main/java/guru/springframework/controllers/IngredientController.java b/src/main/java/guru/springframework/controllers/IngredientController.java index c4196932cd..e882bc9670 100644 --- a/src/main/java/guru/springframework/controllers/IngredientController.java +++ b/src/main/java/guru/springframework/controllers/IngredientController.java @@ -1,20 +1,25 @@ package guru.springframework.controllers; +import guru.springframework.commands.IngredientCommand; +import guru.springframework.services.IngredientService; import guru.springframework.services.RecipeService; +import guru.springframework.services.UnitOfMeasureService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; @Slf4j @Controller public class IngredientController { private final RecipeService recipeService; + private final IngredientService ingredientService; + private final UnitOfMeasureService unitOfMeasureService; - public IngredientController(RecipeService recipeService) { + public IngredientController(RecipeService recipeService, IngredientService ingredientService, UnitOfMeasureService unitOfMeasureService) { this.recipeService = recipeService; + this.ingredientService = ingredientService; + this.unitOfMeasureService = unitOfMeasureService; } @GetMapping @@ -26,4 +31,29 @@ public String listIngredients(@PathVariable String recipeId, Model model){ return "recipe/ingredient/list"; } + + @GetMapping + @RequestMapping("recipe/{recipeId}/ingredient/{id}/show") + public String showRecipeIngredient(@PathVariable String recipeId, @PathVariable String id, Model model){ + model.addAttribute("ingredient",ingredientService.findByRecipeIdAndIngredientId(Long.valueOf(recipeId),Long.valueOf(id))); + return "recipe/ingredient/show"; + } + + @GetMapping + @RequestMapping("recipe/{recipeId}/ingredient/{id}/update") + public String updateRecipeIngredient(@PathVariable String recipeId, @PathVariable String id, Model model){ + model.addAttribute("ingredient",ingredientService.findByRecipeIdAndIngredientId(Long.valueOf(recipeId), Long.valueOf(id))); + model.addAttribute("uomList",unitOfMeasureService.listAllUoms()); + return "recipe/ingredient/ingredientform"; + } + + @PostMapping("recipe/{recipeId}/ingredient") + public String saveOrUpdate(@ModelAttribute IngredientCommand command){ + IngredientCommand savedCommand = ingredientService.saveIngredientCommand(command); + + log.debug("saved recipe id: " + savedCommand.getRecipeId()); + log.debug("saved ingredient id: " + savedCommand.getId()); + + return "redirect:/recipe/" + savedCommand.getRecipeId() + "/ingredient/" + savedCommand.getId() + "/show"; + } } diff --git a/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java index 599989ac5b..364d9561c2 100644 --- a/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java +++ b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java @@ -25,6 +25,9 @@ public IngredientCommand convert(Ingredient ingredient){ IngredientCommand ingredientCommand = new IngredientCommand(); ingredientCommand.setId(ingredient.getId()); + if(ingredient.getRecipe() != null){ + ingredientCommand.setRecipeId(ingredient.getRecipe().getId()); + } ingredientCommand.setAmount(ingredient.getAmount()); ingredientCommand.setDescription(ingredient.getDescription()); ingredientCommand.setUom(uomConverter.convert(ingredient.getUnitOfMeasure())); diff --git a/src/main/java/guru/springframework/services/IngredientService.java b/src/main/java/guru/springframework/services/IngredientService.java new file mode 100644 index 0000000000..1401aafc50 --- /dev/null +++ b/src/main/java/guru/springframework/services/IngredientService.java @@ -0,0 +1,8 @@ +package guru.springframework.services; + +import guru.springframework.commands.IngredientCommand; + +public interface IngredientService { + IngredientCommand findByRecipeIdAndIngredientId(Long recipeId, Long ingredientId); + IngredientCommand saveIngredientCommand(IngredientCommand command); +} diff --git a/src/main/java/guru/springframework/services/IngredientServiceImpl.java b/src/main/java/guru/springframework/services/IngredientServiceImpl.java new file mode 100644 index 0000000000..fcb0f6ea5d --- /dev/null +++ b/src/main/java/guru/springframework/services/IngredientServiceImpl.java @@ -0,0 +1,85 @@ +package guru.springframework.services; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.converters.IngredientCommandToIngredient; +import guru.springframework.converters.IngredientToIngredientCommand; +import guru.springframework.domain.Ingredient; +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; +import guru.springframework.repositories.UnitOfMeasureRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.Optional; + +@Slf4j +@Service +public class IngredientServiceImpl implements IngredientService{ + private final IngredientToIngredientCommand ingredientToIngredientCommand; + private final IngredientCommandToIngredient ingredientCommandToIngredient; + private final RecipeRepository recipeRepository; + private final UnitOfMeasureRepository unitOfMeasureRepository; + + public IngredientServiceImpl(IngredientToIngredientCommand ingredientToIngredientCommand, IngredientCommandToIngredient ingredientCommandToIngredient, RecipeRepository recipeRepository, UnitOfMeasureRepository unitOfMeasureRepository) { + this.ingredientToIngredientCommand = ingredientToIngredientCommand; + this.ingredientCommandToIngredient = ingredientCommandToIngredient; + this.recipeRepository = recipeRepository; + this.unitOfMeasureRepository = unitOfMeasureRepository; + } + + @Override + public IngredientCommand findByRecipeIdAndIngredientId(Long recipeId, Long ingredientId){ + Optional recipeOptional = recipeRepository.findById(recipeId); + + if(!recipeOptional.isPresent()){ + log.error("recipe id not found. Id: " + recipeId); + } + + Recipe recipe = recipeOptional.get(); + + Optional ingredientCommandOptional = recipe.getIngredients().stream() + .filter(ingredient -> ingredient.getId().equals(ingredientId)) + .map( ingredient -> ingredientToIngredientCommand.convert(ingredient)).findFirst(); + + if(!ingredientCommandOptional.isPresent()){ + log.error("Ingredient id not found: " + ingredientId); + } + + return ingredientCommandOptional.get(); + } + @Override + @Transactional + public IngredientCommand saveIngredientCommand(IngredientCommand command){ + Optional recipeOptional = recipeRepository.findById(command.getRecipeId()); + + if(!recipeOptional.isPresent()){ + log.error("Recipe not found for id: " + command.getRecipeId()); + return new IngredientCommand(); + } else{ + Recipe recipe = recipeOptional.get(); + Optional ingredientOptional = recipe + .getIngredients() + .stream() + .filter(ingredient -> ingredient.getId().equals(command.getId())) + .findFirst(); + if(ingredientOptional.isPresent()){ + Ingredient ingredientFound = ingredientOptional.get(); + ingredientFound.setDescription(command.getDescription()); + ingredientFound.setAmount(command.getAmount()); + ingredientFound.setUnitOfMeasure(unitOfMeasureRepository.findById(command.getUom().getId()) + .orElseThrow(()-> new RuntimeException("UOM NOT FOUND"))); + }else{ + recipe.addIngredient(ingredientCommandToIngredient.convert(command)); + } + + Recipe savedRecipe = recipeRepository.save(recipe); + + return ingredientToIngredientCommand.convert(savedRecipe.getIngredients().stream() + .filter(recipeIngredients -> recipeIngredients.getId().equals(command.getId())) + .findFirst() + .get()); + } + } + +} diff --git a/src/main/java/guru/springframework/services/UnitOfMeasureService.java b/src/main/java/guru/springframework/services/UnitOfMeasureService.java new file mode 100644 index 0000000000..6fea70337d --- /dev/null +++ b/src/main/java/guru/springframework/services/UnitOfMeasureService.java @@ -0,0 +1,9 @@ +package guru.springframework.services; + +import guru.springframework.commands.UnitOfMeasureCommand; + +import java.util.Set; + +public interface UnitOfMeasureService { + Set listAllUoms(); +} diff --git a/src/main/java/guru/springframework/services/UnitOfMeasureServiceImpl.java b/src/main/java/guru/springframework/services/UnitOfMeasureServiceImpl.java new file mode 100644 index 0000000000..a8cc3c9c1c --- /dev/null +++ b/src/main/java/guru/springframework/services/UnitOfMeasureServiceImpl.java @@ -0,0 +1,29 @@ +package guru.springframework.services; + +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.converters.UnitOfMeasureToUnitOfMeasureCommand; +import guru.springframework.repositories.UnitOfMeasureRepository; +import org.springframework.stereotype.Service; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +@Service +public class UnitOfMeasureServiceImpl implements UnitOfMeasureService{ + private final UnitOfMeasureRepository unitOfMeasureRepository; + private final UnitOfMeasureToUnitOfMeasureCommand unitOfMeasureToUnitOfMeasureCommand; + + public UnitOfMeasureServiceImpl(UnitOfMeasureRepository unitOfMeasureRepository, UnitOfMeasureToUnitOfMeasureCommand unitOfMeasureToUnitOfMeasureCommand) { + this.unitOfMeasureRepository = unitOfMeasureRepository; + this.unitOfMeasureToUnitOfMeasureCommand = unitOfMeasureToUnitOfMeasureCommand; + } + + @Override + public Set listAllUoms(){ + return StreamSupport.stream(unitOfMeasureRepository.findAll() + .spliterator(),false) + .map(unitOfMeasureToUnitOfMeasureCommand::convert) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/resources/templates/recipe/ingredient/ingredientform.html b/src/main/resources/templates/recipe/ingredient/ingredientform.html new file mode 100644 index 0000000000..740fc354f5 --- /dev/null +++ b/src/main/resources/templates/recipe/ingredient/ingredientform.html @@ -0,0 +1,61 @@ + + + + + View Ingredient + + + + + + + +
+
+
+ +
+ +
+
+
+

Edit Ingredient Information

+
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/ingredient/show.html b/src/main/resources/templates/recipe/ingredient/show.html new file mode 100644 index 0000000000..350eea0b89 --- /dev/null +++ b/src/main/resources/templates/recipe/ingredient/show.html @@ -0,0 +1,55 @@ + + + + + View Ingredient + + + + + + + +
+
+
+
+
+
+

Ingredient

+
+
+ +
+ + + + + + + + + + + + + + + + + + +
IDDescription
123Tasty Goodnees 1ViewUpdateDelete
334Tasty Goodness 3
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java index fce28b634d..72e36b0dc8 100644 --- a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java +++ b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java @@ -1,21 +1,33 @@ package guru.springframework.controllers; +import guru.springframework.commands.IngredientCommand; import guru.springframework.commands.RecipeCommand; +import guru.springframework.services.IngredientService; import guru.springframework.services.RecipeService; +import guru.springframework.services.UnitOfMeasureService; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.HashSet; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; public class IngredientControllerTest { + @Mock + IngredientService ingredientService; + @Mock + UnitOfMeasureService unitOfMeasureService; @Mock RecipeService recipeService; IngredientController controller; @@ -23,7 +35,7 @@ public class IngredientControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - controller = new IngredientController(recipeService); + controller = new IngredientController(recipeService,ingredientService,unitOfMeasureService); mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } @@ -39,4 +51,47 @@ public void testListIngredients() throws Exception { verify(recipeService,times(1)).findCommandById(anyLong()); } + + @Test + public void testShowIngredient() throws Exception{ + IngredientCommand ingredientCommand = new IngredientCommand(); + + when(ingredientService.findByRecipeIdAndIngredientId(anyLong(),anyLong())).thenReturn(ingredientCommand); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/ingredient/2/show")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/ingredient/show")) + .andExpect(MockMvcResultMatchers.model().attributeExists("ingredient")); + } + @Test + public void testUpdateIngredientForm() throws Exception { + IngredientCommand ingredientCommand = new IngredientCommand(); + + when(ingredientService.findByRecipeIdAndIngredientId(anyLong(),anyLong())).thenReturn(ingredientCommand); + when(unitOfMeasureService.listAllUoms()).thenReturn(new HashSet<>()); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/ingredient/2/update")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/ingredient/ingredientform")) + .andExpect(MockMvcResultMatchers.model().attributeExists("ingredient")) + .andExpect(MockMvcResultMatchers.model().attributeExists("uomList")); + } + + @Test + public void testSaveOrUpdate() throws Exception{ + IngredientCommand command = new IngredientCommand(); + command.setId(3L); + command.setRecipeId(2L); + + when(ingredientService.saveIngredientCommand(any())).thenReturn(command); + + mockMvc.perform(MockMvcRequestBuilders.post("/recipe/2/ingredient") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("id","") + .param("description","some string") + ) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/2/ingredient/3/show")); + + } } \ No newline at end of file diff --git a/src/test/java/guru/springframework/services/IngredientServiceImplTest.java b/src/test/java/guru/springframework/services/IngredientServiceImplTest.java new file mode 100644 index 0000000000..02f2537fb9 --- /dev/null +++ b/src/test/java/guru/springframework/services/IngredientServiceImplTest.java @@ -0,0 +1,105 @@ +package guru.springframework.services; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.converters.IngredientCommandToIngredient; +import guru.springframework.converters.IngredientToIngredientCommand; +import guru.springframework.converters.UnitOfMeasureCommandToUnitOfMeasure; +import guru.springframework.converters.UnitOfMeasureToUnitOfMeasureCommand; +import guru.springframework.domain.Ingredient; +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; +import guru.springframework.repositories.UnitOfMeasureRepository; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.*; + +public class IngredientServiceImplTest { + private final IngredientToIngredientCommand ingredientToIngredientCommand; + private final IngredientCommandToIngredient ingredientCommandToIngredient; + + @Mock + RecipeRepository recipeRepository; + @Mock + UnitOfMeasureRepository unitOfMeasureRepository; + IngredientService ingredientService; + + public IngredientServiceImplTest(){ + this.ingredientToIngredientCommand = new IngredientToIngredientCommand(new UnitOfMeasureToUnitOfMeasureCommand()); + this.ingredientCommandToIngredient = new IngredientCommandToIngredient(new UnitOfMeasureCommandToUnitOfMeasure()); + } + @Before + public void setUp() throws Exception{ + MockitoAnnotations.initMocks(this); + ingredientService = new IngredientServiceImpl(ingredientToIngredientCommand, ingredientCommandToIngredient, recipeRepository, unitOfMeasureRepository); + } + + @Test + public void findByRecipeIdAndId() throws Exception{ + + } + @Test + public void findByRecipeIdAndReceipeIdHappyPath() throws Exception { + //given + Recipe recipe = new Recipe(); + recipe.setId(1L); + + Ingredient ingredient1 = new Ingredient(); + ingredient1.setId(1L); + + Ingredient ingredient2 = new Ingredient(); + ingredient2.setId(1L); + + Ingredient ingredient3 = new Ingredient(); + ingredient3.setId(3L); + + recipe.addIngredient(ingredient1); + recipe.addIngredient(ingredient2); + recipe.addIngredient(ingredient3); + Optional recipeOptional = Optional.of(recipe); + + when(recipeRepository.findById(anyLong())).thenReturn(recipeOptional); + + //then + IngredientCommand ingredientCommand = ingredientService.findByRecipeIdAndIngredientId(1L, 3L); + + //when + assertEquals(Long.valueOf(3L), ingredientCommand.getId()); + assertEquals(Long.valueOf(1L), ingredientCommand.getRecipeId()); + verify(recipeRepository, times(1)).findById(anyLong()); + } + + + @Test + public void testSaveRecipeCommand() throws Exception { + //given + IngredientCommand command = new IngredientCommand(); + command.setId(3L); + command.setRecipeId(2L); + + Optional recipeOptional = Optional.of(new Recipe()); + + Recipe savedRecipe = new Recipe(); + savedRecipe.addIngredient(new Ingredient()); + savedRecipe.getIngredients().iterator().next().setId(3L); + + when(recipeRepository.findById(anyLong())).thenReturn(recipeOptional); + when(recipeRepository.save(any())).thenReturn(savedRecipe); + + //when + IngredientCommand savedCommand = ingredientService.saveIngredientCommand(command); + + //then + assertEquals(Long.valueOf(3L), savedCommand.getId()); + verify(recipeRepository, times(1)).findById(anyLong()); + verify(recipeRepository, times(1)).save(any(Recipe.class)); + } +} diff --git a/src/test/java/guru/springframework/services/UnitOfMeasureServiceImplTest.java b/src/test/java/guru/springframework/services/UnitOfMeasureServiceImplTest.java new file mode 100644 index 0000000000..2b9de9530d --- /dev/null +++ b/src/test/java/guru/springframework/services/UnitOfMeasureServiceImplTest.java @@ -0,0 +1,47 @@ +package guru.springframework.services; + +import guru.springframework.commands.UnitOfMeasureCommand; +import guru.springframework.converters.UnitOfMeasureToUnitOfMeasureCommand; +import guru.springframework.domain.UnitOfMeasure; +import guru.springframework.repositories.UnitOfMeasureRepository; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashSet; +import java.util.Set; + +import static junit.framework.TestCase.assertEquals; +import static org.mockito.Mockito.*; + +public class UnitOfMeasureServiceImplTest { + UnitOfMeasureToUnitOfMeasureCommand unitOfMeasureToUnitOfMeasureCommand = new UnitOfMeasureToUnitOfMeasureCommand(); + UnitOfMeasureService service; + @Mock + UnitOfMeasureRepository unitOfMeasureRepository; + @Before + public void setUp() throws Exception{ + MockitoAnnotations.initMocks(this); + service = new UnitOfMeasureServiceImpl(unitOfMeasureRepository,unitOfMeasureToUnitOfMeasureCommand); + } + + @Test + public void listAllUoms() throws Exception{ + Set unitOfMeasures = new HashSet<>(); + UnitOfMeasure uom1 = new UnitOfMeasure(); + uom1.setId(1L); + unitOfMeasures.add(uom1); + + UnitOfMeasure uom2 = new UnitOfMeasure(); + uom2.setId(2L); + unitOfMeasures.add(uom2); + + when(unitOfMeasureRepository.findAll()).thenReturn(unitOfMeasures); + + Set commands = service.listAllUoms(); + + assertEquals(2,commands.size()); + verify(unitOfMeasureRepository,times(1)).findAll(); + } +} From b02774b71094f560942810c49c9dac48acfeea3d Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 1 Dec 2023 10:35:51 -0600 Subject: [PATCH 18/36] New Ingredients --- .../controllers/IngredientController.java | 18 +++++++++++++++ .../guru/springframework/domain/Category.java | 3 ++- .../springframework/domain/Ingredient.java | 5 ++++- .../guru/springframework/domain/Notes.java | 5 ++++- .../guru/springframework/domain/Recipe.java | 5 ++++- .../springframework/domain/UnitOfMeasure.java | 5 ++++- .../services/IngredientServiceImpl.java | 22 +++++++++++++++---- .../controllers/IngredientControllerTest.java | 18 +++++++++++++++ 8 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/main/java/guru/springframework/controllers/IngredientController.java b/src/main/java/guru/springframework/controllers/IngredientController.java index e882bc9670..5835ec1e8f 100644 --- a/src/main/java/guru/springframework/controllers/IngredientController.java +++ b/src/main/java/guru/springframework/controllers/IngredientController.java @@ -1,6 +1,8 @@ package guru.springframework.controllers; import guru.springframework.commands.IngredientCommand; +import guru.springframework.commands.RecipeCommand; +import guru.springframework.commands.UnitOfMeasureCommand; import guru.springframework.services.IngredientService; import guru.springframework.services.RecipeService; import guru.springframework.services.UnitOfMeasureService; @@ -39,6 +41,22 @@ public String showRecipeIngredient(@PathVariable String recipeId, @PathVariable return "recipe/ingredient/show"; } + @GetMapping + @RequestMapping("recipe/{recipeId}/ingredient/new") + public String newIngredient(@PathVariable String recipeId, Model model){ + RecipeCommand recipeCommand = recipeService.findCommandById(Long.valueOf(recipeId)); + + //return back parent id + IngredientCommand ingredientCommand = new IngredientCommand(); + ingredientCommand.setRecipeId(Long.valueOf(recipeId)); + model.addAttribute("ingredient",ingredientCommand); + + //init uom + ingredientCommand.setUom(new UnitOfMeasureCommand()); + model.addAttribute("uomList",unitOfMeasureService.listAllUoms()); + return "recipe/ingredient/ingredientform"; + } + @GetMapping @RequestMapping("recipe/{recipeId}/ingredient/{id}/update") public String updateRecipeIngredient(@PathVariable String recipeId, @PathVariable String id, Model model){ diff --git a/src/main/java/guru/springframework/domain/Category.java b/src/main/java/guru/springframework/domain/Category.java index 4942026119..a093645566 100644 --- a/src/main/java/guru/springframework/domain/Category.java +++ b/src/main/java/guru/springframework/domain/Category.java @@ -5,7 +5,8 @@ import javax.persistence.*; import java.util.Set; -@Data +@Getter +@Setter @EqualsAndHashCode(exclude = {"recipes"}) @Entity public class Category { diff --git a/src/main/java/guru/springframework/domain/Ingredient.java b/src/main/java/guru/springframework/domain/Ingredient.java index 13de1a55f7..5251923a03 100644 --- a/src/main/java/guru/springframework/domain/Ingredient.java +++ b/src/main/java/guru/springframework/domain/Ingredient.java @@ -2,10 +2,13 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; import javax.persistence.*; import java.math.BigDecimal; -@Data +@Getter +@Setter @EqualsAndHashCode(exclude = {"recipe"}) @Entity public class Ingredient { diff --git a/src/main/java/guru/springframework/domain/Notes.java b/src/main/java/guru/springframework/domain/Notes.java index c7374cc4a0..a912687bb6 100644 --- a/src/main/java/guru/springframework/domain/Notes.java +++ b/src/main/java/guru/springframework/domain/Notes.java @@ -2,9 +2,12 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; import javax.persistence.*; -@Data +@Getter +@Setter @EqualsAndHashCode(exclude = {"recipe"}) @Entity public class Notes { diff --git a/src/main/java/guru/springframework/domain/Recipe.java b/src/main/java/guru/springframework/domain/Recipe.java index 17ed3a5e38..d3557f8b7d 100644 --- a/src/main/java/guru/springframework/domain/Recipe.java +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -1,11 +1,14 @@ package guru.springframework.domain; import lombok.Data; +import lombok.Getter; +import lombok.Setter; import javax.persistence.*; import java.util.HashSet; import java.util.Set; -@Data +@Getter +@Setter @Entity public class Recipe { //primary key generada con la estrategia de identidad diff --git a/src/main/java/guru/springframework/domain/UnitOfMeasure.java b/src/main/java/guru/springframework/domain/UnitOfMeasure.java index 89294611a6..a6bfbf8095 100644 --- a/src/main/java/guru/springframework/domain/UnitOfMeasure.java +++ b/src/main/java/guru/springframework/domain/UnitOfMeasure.java @@ -1,9 +1,12 @@ package guru.springframework.domain; import lombok.Data; +import lombok.Getter; +import lombok.Setter; import javax.persistence.*; -@Data +@Getter +@Setter @Entity public class UnitOfMeasure { @Id diff --git a/src/main/java/guru/springframework/services/IngredientServiceImpl.java b/src/main/java/guru/springframework/services/IngredientServiceImpl.java index fcb0f6ea5d..860868713d 100644 --- a/src/main/java/guru/springframework/services/IngredientServiceImpl.java +++ b/src/main/java/guru/springframework/services/IngredientServiceImpl.java @@ -70,15 +70,29 @@ public IngredientCommand saveIngredientCommand(IngredientCommand command){ ingredientFound.setUnitOfMeasure(unitOfMeasureRepository.findById(command.getUom().getId()) .orElseThrow(()-> new RuntimeException("UOM NOT FOUND"))); }else{ - recipe.addIngredient(ingredientCommandToIngredient.convert(command)); + //agregar ingrediente nuevo + Ingredient ingredient = ingredientCommandToIngredient.convert(command); + ingredient.setRecipe(recipe); + recipe.addIngredient(ingredient); } Recipe savedRecipe = recipeRepository.save(recipe); - return ingredientToIngredientCommand.convert(savedRecipe.getIngredients().stream() + Optional savedIngredientOptional = savedRecipe.getIngredients().stream() .filter(recipeIngredients -> recipeIngredients.getId().equals(command.getId())) - .findFirst() - .get()); + .findFirst(); + + //checar por descripcion + if(!savedIngredientOptional.isPresent()){ + savedIngredientOptional = savedRecipe.getIngredients().stream() + .filter(recipeIngredients -> recipeIngredients.getDescription().equals(command.getDescription())) + .filter(recipeIngredients -> recipeIngredients.getAmount().equals(command.getAmount())) + .filter(recipeIngredients -> recipeIngredients.getUnitOfMeasure().getId().equals(command.getUom().getId())) + .findFirst(); + } + + // checar fallas + return ingredientToIngredientCommand.convert(savedIngredientOptional.get()); } } diff --git a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java index 72e36b0dc8..72c0bf1e3f 100644 --- a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java +++ b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java @@ -63,6 +63,24 @@ public void testShowIngredient() throws Exception{ .andExpect(MockMvcResultMatchers.view().name("recipe/ingredient/show")) .andExpect(MockMvcResultMatchers.model().attributeExists("ingredient")); } + + @Test + public void testNewIngredientForm() throws Exception { + RecipeCommand recipeCommand = new RecipeCommand(); + recipeCommand.setId(1L); + + when(recipeService.findCommandById(anyLong())).thenReturn(recipeCommand); + when(unitOfMeasureService.listAllUoms()).thenReturn(new HashSet<>()); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/ingredient/new")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.view().name("recipe/ingredient/ingredientform")) + .andExpect(MockMvcResultMatchers.model().attributeExists("ingredient")) + .andExpect(MockMvcResultMatchers.model().attributeExists("uomList")); + + verify(recipeService, times(1)).findCommandById(anyLong()); + } + @Test public void testUpdateIngredientForm() throws Exception { IngredientCommand ingredientCommand = new IngredientCommand(); From d615007ceb6fc55006dc8f81be5b1a6d1211d781 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 1 Dec 2023 11:20:33 -0600 Subject: [PATCH 19/36] Delete Ingredients --- .../controllers/IngredientController.java | 9 ++++++ .../services/IngredientService.java | 1 + .../services/IngredientServiceImpl.java | 28 +++++++++++++++++++ .../controllers/IngredientControllerTest.java | 9 ++++++ .../services/IngredientServiceImplTest.java | 17 +++++++++++ 5 files changed, 64 insertions(+) diff --git a/src/main/java/guru/springframework/controllers/IngredientController.java b/src/main/java/guru/springframework/controllers/IngredientController.java index 5835ec1e8f..70ab5302f6 100644 --- a/src/main/java/guru/springframework/controllers/IngredientController.java +++ b/src/main/java/guru/springframework/controllers/IngredientController.java @@ -74,4 +74,13 @@ public String saveOrUpdate(@ModelAttribute IngredientCommand command){ return "redirect:/recipe/" + savedCommand.getRecipeId() + "/ingredient/" + savedCommand.getId() + "/show"; } + + @GetMapping + @RequestMapping("recipe/{recipeId}/ingredient/{id}/delete") + public String deleteIngredient(@PathVariable String recipeId, @PathVariable String id){ + log.debug("deleting ingredient id: " + id); + ingredientService.deleteById(Long.valueOf(recipeId), Long.valueOf(id)); + return "redirect:/recipe/" + recipeId + "/ingredients"; + } + } diff --git a/src/main/java/guru/springframework/services/IngredientService.java b/src/main/java/guru/springframework/services/IngredientService.java index 1401aafc50..3f24889c73 100644 --- a/src/main/java/guru/springframework/services/IngredientService.java +++ b/src/main/java/guru/springframework/services/IngredientService.java @@ -5,4 +5,5 @@ public interface IngredientService { IngredientCommand findByRecipeIdAndIngredientId(Long recipeId, Long ingredientId); IngredientCommand saveIngredientCommand(IngredientCommand command); + void deleteById(Long recipeId, Long idToDelete); } diff --git a/src/main/java/guru/springframework/services/IngredientServiceImpl.java b/src/main/java/guru/springframework/services/IngredientServiceImpl.java index 860868713d..3275bbc3c5 100644 --- a/src/main/java/guru/springframework/services/IngredientServiceImpl.java +++ b/src/main/java/guru/springframework/services/IngredientServiceImpl.java @@ -96,4 +96,32 @@ public IngredientCommand saveIngredientCommand(IngredientCommand command){ } } + @Override + public void deleteById(Long recipeId, Long idToDelete){ + log.debug("Deleting ingredient: " + recipeId + ":" + idToDelete); + + Optional recipeOptional = recipeRepository.findById(recipeId); + + if(recipeOptional.isPresent()){ + Recipe recipe = recipeOptional.get(); + log.debug("found recipe"); + + Optional ingredientOptional = recipe + .getIngredients() + .stream() + .filter(ingredient -> ingredient.getId().equals(idToDelete)) + .findFirst(); + + if(ingredientOptional.isPresent()){ + log.debug("found ingredient"); + Ingredient ingredientToDelete = ingredientOptional.get(); + ingredientToDelete.setRecipe(null); + recipe.getIngredients().remove(ingredientOptional.get()); + recipeRepository.save(recipe); + } + } else{ + log.debug("Recipe Id Not Found. Id: " + recipeId); + } + } + } diff --git a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java index 72c0bf1e3f..19df76d1c7 100644 --- a/src/test/java/guru/springframework/controllers/IngredientControllerTest.java +++ b/src/test/java/guru/springframework/controllers/IngredientControllerTest.java @@ -112,4 +112,13 @@ public void testSaveOrUpdate() throws Exception{ .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/2/ingredient/3/show")); } + + @Test + public void testDeleteIngredient() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/2/ingredient/3/delete")) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/2/ingredients")); + + verify(ingredientService,times(1)).deleteById(anyLong(),anyLong()); + } } \ No newline at end of file diff --git a/src/test/java/guru/springframework/services/IngredientServiceImplTest.java b/src/test/java/guru/springframework/services/IngredientServiceImplTest.java index 02f2537fb9..81ca9d6814 100644 --- a/src/test/java/guru/springframework/services/IngredientServiceImplTest.java +++ b/src/test/java/guru/springframework/services/IngredientServiceImplTest.java @@ -102,4 +102,21 @@ public void testSaveRecipeCommand() throws Exception { verify(recipeRepository, times(1)).findById(anyLong()); verify(recipeRepository, times(1)).save(any(Recipe.class)); } + + @Test + public void testDeleteById() throws Exception { + Recipe recipe = new Recipe(); + Ingredient ingredient = new Ingredient(); + ingredient.setId(3L); + recipe.addIngredient(ingredient); + ingredient.setRecipe(recipe); + Optional recipeOptional = Optional.of(recipe); + + when(recipeRepository.findById(anyLong())).thenReturn(recipeOptional); + + ingredientService.deleteById(1L,3L); + + verify(recipeRepository,times(1)).findById(anyLong()); + verify(recipeRepository,times(1)).save(any(Recipe.class)); + } } From f492815618a9318593abd476fced6f17d25b68f4 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Tue, 5 Dec 2023 11:45:31 -0600 Subject: [PATCH 20/36] Refactor some controllers --- .../controllers/IngredientController.java | 15 +++++---------- .../controllers/RecipeController.java | 15 +++++---------- .../resources/templates/recipe/recipeform.html | 8 +++++++- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/main/java/guru/springframework/controllers/IngredientController.java b/src/main/java/guru/springframework/controllers/IngredientController.java index 70ab5302f6..fea770e7c9 100644 --- a/src/main/java/guru/springframework/controllers/IngredientController.java +++ b/src/main/java/guru/springframework/controllers/IngredientController.java @@ -24,8 +24,7 @@ public IngredientController(RecipeService recipeService, IngredientService ingre this.unitOfMeasureService = unitOfMeasureService; } - @GetMapping - @RequestMapping("/recipe/{recipeId}/ingredients") + @GetMapping("/recipe/{recipeId}/ingredients") public String listIngredients(@PathVariable String recipeId, Model model){ log.debug("Getting ingredient list for recipe id: "+recipeId); @@ -34,15 +33,13 @@ public String listIngredients(@PathVariable String recipeId, Model model){ return "recipe/ingredient/list"; } - @GetMapping - @RequestMapping("recipe/{recipeId}/ingredient/{id}/show") + @GetMapping("recipe/{recipeId}/ingredient/{id}/show") public String showRecipeIngredient(@PathVariable String recipeId, @PathVariable String id, Model model){ model.addAttribute("ingredient",ingredientService.findByRecipeIdAndIngredientId(Long.valueOf(recipeId),Long.valueOf(id))); return "recipe/ingredient/show"; } - @GetMapping - @RequestMapping("recipe/{recipeId}/ingredient/new") + @GetMapping("recipe/{recipeId}/ingredient/new") public String newIngredient(@PathVariable String recipeId, Model model){ RecipeCommand recipeCommand = recipeService.findCommandById(Long.valueOf(recipeId)); @@ -57,8 +54,7 @@ public String newIngredient(@PathVariable String recipeId, Model model){ return "recipe/ingredient/ingredientform"; } - @GetMapping - @RequestMapping("recipe/{recipeId}/ingredient/{id}/update") + @GetMapping("recipe/{recipeId}/ingredient/{id}/update") public String updateRecipeIngredient(@PathVariable String recipeId, @PathVariable String id, Model model){ model.addAttribute("ingredient",ingredientService.findByRecipeIdAndIngredientId(Long.valueOf(recipeId), Long.valueOf(id))); model.addAttribute("uomList",unitOfMeasureService.listAllUoms()); @@ -75,8 +71,7 @@ public String saveOrUpdate(@ModelAttribute IngredientCommand command){ return "redirect:/recipe/" + savedCommand.getRecipeId() + "/ingredient/" + savedCommand.getId() + "/show"; } - @GetMapping - @RequestMapping("recipe/{recipeId}/ingredient/{id}/delete") + @GetMapping("recipe/{recipeId}/ingredient/{id}/delete") public String deleteIngredient(@PathVariable String recipeId, @PathVariable String id){ log.debug("deleting ingredient id: " + id); ingredientService.deleteById(Long.valueOf(recipeId), Long.valueOf(id)); diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index 37e138e16a..0699df50b2 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -15,35 +15,30 @@ public class RecipeController { public RecipeController(RecipeService recipeService) { this.recipeService = recipeService; } - @GetMapping - @RequestMapping("/recipe/{id}/show") + @GetMapping("/recipe/{id}/show") public String showById(@PathVariable String id, Model model){ model.addAttribute("recipe",recipeService.findById(Long.valueOf(id))); return "recipe/show"; } - @GetMapping - @RequestMapping("recipe/new") + @GetMapping("recipe/new") public String newRecipe(Model model){ model.addAttribute("recipe", new RecipeCommand()); return "recipe/recipeform"; } - @GetMapping - @RequestMapping("recipe/{id}/update") + @GetMapping("recipe/{id}/update") public String updateRecipe(@PathVariable String id, Model model){ model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(id))); return "recipe/recipeform"; } - @PostMapping - @RequestMapping("recipe") + @PostMapping("recipe") public String serveOrUpdate(@ModelAttribute RecipeCommand command){ RecipeCommand savedCommand = recipeService.saveRecipeCommand(command); return "redirect:/recipe/" + savedCommand.getId() + "/show"; } - @GetMapping - @RequestMapping("recipe/{id}/delete") + @GetMapping("recipe/{id}/delete") public String deleteById(@PathVariable String id){ log.debug("Deleting id: " +id); diff --git a/src/main/resources/templates/recipe/recipeform.html b/src/main/resources/templates/recipe/recipeform.html index 7e1b794f1d..6c156167d5 100644 --- a/src/main/resources/templates/recipe/recipeform.html +++ b/src/main/resources/templates/recipe/recipeform.html @@ -63,7 +63,13 @@

Edit Recipe Information

- + + + + + +
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html index e17190a6f4..46e7a11ecf 100644 --- a/src/main/resources/templates/recipe/show.html +++ b/src/main/resources/templates/recipe/show.html @@ -23,6 +23,10 @@
Recipe Description Here!
+
@@ -36,6 +40,11 @@
  • cat three
  • +
    + +
    diff --git a/src/test/java/guru/springframework/controllers/ImageControllerTest.java b/src/test/java/guru/springframework/controllers/ImageControllerTest.java new file mode 100644 index 0000000000..5adf77c264 --- /dev/null +++ b/src/test/java/guru/springframework/controllers/ImageControllerTest.java @@ -0,0 +1,61 @@ +package guru.springframework.controllers; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.services.ImageService; +import guru.springframework.services.RecipeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.*; + +public class ImageControllerTest { + @Mock + ImageService imageService; + @Mock + RecipeService recipeService; + + ImageController controller; + MockMvc mockMvc; + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + controller = new ImageController(imageService,recipeService); + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + @Test + public void getImageForm() throws Exception { + RecipeCommand command = new RecipeCommand(); + command.setId(1L); + + when(recipeService.findCommandById(anyLong())).thenReturn(command); + + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/image")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + + verify(recipeService,times(1)).findCommandById(anyLong()); + } + @Test + public void handleImagePost() throws Exception { + MockMultipartFile multipartFile = + new MockMultipartFile("imagefile", "testing.txt", "text/plain", + "Spring Framework Guru".getBytes()); + + mockMvc.perform(MockMvcRequestBuilders.multipart("/recipe/1/image").file(multipartFile)) + .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) + .andExpect(MockMvcResultMatchers.header().string("Location", "/recipe/1/show")); + + verify(imageService, times(1)).saveImageFile(anyLong(), any()); + } + +} From bd0974185c472d0f9d6a81de7e067a32685dcb41 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 6 Dec 2023 08:58:51 -0600 Subject: [PATCH 22/36] Images into database --- .../services/ImageServiceImpl.java | 27 ++++++++++- .../services/ImageServiceImplTest.java | 46 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/test/java/guru/springframework/services/ImageServiceImplTest.java diff --git a/src/main/java/guru/springframework/services/ImageServiceImpl.java b/src/main/java/guru/springframework/services/ImageServiceImpl.java index b6ab4caa78..1b84ca0eb2 100644 --- a/src/main/java/guru/springframework/services/ImageServiceImpl.java +++ b/src/main/java/guru/springframework/services/ImageServiceImpl.java @@ -1,14 +1,39 @@ package guru.springframework.services; +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import javax.transaction.Transactional; +import java.io.IOException; + @Slf4j @Service public class ImageServiceImpl implements ImageService{ + private final RecipeRepository recipeRepository; + + public ImageServiceImpl(RecipeRepository recipeRepository) { + this.recipeRepository = recipeRepository; + } + @Override + @Transactional public void saveImageFile(Long recipeId, MultipartFile file){ - log.debug("Received a file"); + try{ + Recipe recipe = recipeRepository.findById(recipeId).get(); + Byte[] byteObjects = new Byte[file.getBytes().length]; + int i = 0; + + for(byte b : file.getBytes()){ + byteObjects[i++] = b; + } + recipe.setImage(byteObjects); + recipeRepository.save(recipe); + } catch(IOException e) { + log.error("Error Occurred",e); + e.printStackTrace(); + } } } diff --git a/src/test/java/guru/springframework/services/ImageServiceImplTest.java b/src/test/java/guru/springframework/services/ImageServiceImplTest.java new file mode 100644 index 0000000000..c9830de7e4 --- /dev/null +++ b/src/test/java/guru/springframework/services/ImageServiceImplTest.java @@ -0,0 +1,46 @@ +package guru.springframework.services; + +import guru.springframework.domain.Recipe; +import guru.springframework.repositories.RecipeRepository; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; + +public class ImageServiceImplTest { + @Mock + RecipeRepository recipeRepository; + ImageService imageService; + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + imageService = new ImageServiceImpl(recipeRepository); + } + @Test + public void saveImageFile() throws Exception { + Long id = 1L; + MultipartFile multipartFile = new MockMultipartFile("imagefile","testing.txt","text/plain","Spring Framework Guru".getBytes()); + + Recipe recipe = new Recipe(); + recipe.setId(id); + Optional recipeOptional = Optional.of(recipe); + + when(recipeRepository.findById(anyLong())).thenReturn(recipeOptional); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Recipe.class); + + imageService.saveImageFile(id,multipartFile); + + verify(recipeRepository,times(1)).save(argumentCaptor.capture()); + Recipe savedRecipe = argumentCaptor.getValue(); + assertEquals(multipartFile.getBytes().length,savedRecipe.getImage().length); + } +} From 8531f0142c188bd88841d36bbb85498b6c6f838e Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 6 Dec 2023 09:55:34 -0600 Subject: [PATCH 23/36] Display images --- .../commands/RecipeCommand.java | 1 + .../controllers/ImageController.java | 24 +++++++++++++++++++ .../converters/RecipeToRecipeCommand.java | 1 + .../controllers/ImageControllerTest.java | 24 +++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/src/main/java/guru/springframework/commands/RecipeCommand.java b/src/main/java/guru/springframework/commands/RecipeCommand.java index b14412f9ab..bd0971a14b 100644 --- a/src/main/java/guru/springframework/commands/RecipeCommand.java +++ b/src/main/java/guru/springframework/commands/RecipeCommand.java @@ -21,6 +21,7 @@ public class RecipeCommand { private String url; private String directions; private Set ingredients = new HashSet<>(); + private Byte[] image; private Difficulty difficulty; private NotesCommand notes; private Set categories = new HashSet<>(); diff --git a/src/main/java/guru/springframework/controllers/ImageController.java b/src/main/java/guru/springframework/controllers/ImageController.java index 66aeee1ca9..dc12ae70f2 100644 --- a/src/main/java/guru/springframework/controllers/ImageController.java +++ b/src/main/java/guru/springframework/controllers/ImageController.java @@ -1,7 +1,9 @@ package guru.springframework.controllers; +import guru.springframework.commands.RecipeCommand; import guru.springframework.services.ImageService; import guru.springframework.services.RecipeService; +import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -10,6 +12,11 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + @Controller public class ImageController { private final ImageService imageService; @@ -29,4 +36,21 @@ public String handleImagePost(@PathVariable String id, @RequestParam("imagefile" imageService.saveImageFile(Long.valueOf(id),file); return "redirect:/recipe/" + id + "/show"; } + @GetMapping("recipe/{id}/recipeimage") + public void renderImageFromDB(@PathVariable String id, HttpServletResponse response) throws IOException { + RecipeCommand recipeCommand = recipeService.findCommandById(Long.valueOf(id)); + + if(recipeCommand.getImage() != null){ + byte[] byteArray = new byte[recipeCommand.getImage().length]; + int i = 0; + + for(Byte wrappedByte : recipeCommand.getImage()){ + byteArray[i++] = wrappedByte; + } + + response.setContentType("image/jpeg"); + InputStream is = new ByteArrayInputStream(byteArray); + IOUtils.copy(is,response.getOutputStream()); + } + } } diff --git a/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java b/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java index ff85c5293e..4cbd3ba93b 100644 --- a/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java +++ b/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java @@ -38,6 +38,7 @@ public RecipeCommand convert(Recipe source){ command.setServings(source.getServings()); command.setSource(source.getSource()); command.setUrl(source.getUrl()); + command.setImage(source.getImage()); command.setNotes(notesConverter.convert(source.getNotes())); if(source.getCategories() != null && source.getCategories().size() > 0){ diff --git a/src/test/java/guru/springframework/controllers/ImageControllerTest.java b/src/test/java/guru/springframework/controllers/ImageControllerTest.java index 5adf77c264..28a3a437ad 100644 --- a/src/test/java/guru/springframework/controllers/ImageControllerTest.java +++ b/src/test/java/guru/springframework/controllers/ImageControllerTest.java @@ -7,6 +7,7 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -15,6 +16,7 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; @@ -57,5 +59,27 @@ public void handleImagePost() throws Exception { verify(imageService, times(1)).saveImageFile(anyLong(), any()); } + @Test + public void renderImageFromDB() throws Exception { + RecipeCommand command = new RecipeCommand(); + command.setId(1L); + + String s = "fake image text"; + Byte[] bytesBoxed = new Byte[s.getBytes().length]; + int i = 0; + + for(byte primByte : s.getBytes()){ + bytesBoxed[i++] = primByte; + } + command.setImage(bytesBoxed); + when(recipeService.findCommandById(anyLong())).thenReturn(command); + + MockHttpServletResponse response = mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/recipeimage")) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andReturn().getResponse(); + + byte[] responseBytes = response.getContentAsByteArray(); + assertEquals(s.getBytes().length,responseBytes.length); + } } From 2d41da03646b170b4be065b2824db032bd8f225b Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Wed, 6 Dec 2023 09:59:43 -0600 Subject: [PATCH 24/36] Display images - solved --- src/main/resources/templates/recipe/show.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html index 46e7a11ecf..d8b8a3453a 100644 --- a/src/main/resources/templates/recipe/show.html +++ b/src/main/resources/templates/recipe/show.html @@ -42,7 +42,7 @@
    From cd315e9c5e59218e8d5d6de79f16a6b14eecb6c1 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 09:10:23 -0600 Subject: [PATCH 25/36] Recipe Not Found Exception Handling --- .../exceptions/NotFoundException.java | 17 +++++++++++++++++ .../services/RecipeServiceImpl.java | 4 ++-- .../controllers/RecipeControllerTest.java | 11 +++++++++-- .../services/RecipeServiceImplTest.java | 8 +++++++- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 src/main/java/guru/springframework/exceptions/NotFoundException.java diff --git a/src/main/java/guru/springframework/exceptions/NotFoundException.java b/src/main/java/guru/springframework/exceptions/NotFoundException.java new file mode 100644 index 0000000000..230ff0e965 --- /dev/null +++ b/src/main/java/guru/springframework/exceptions/NotFoundException.java @@ -0,0 +1,17 @@ +package guru.springframework.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class NotFoundException extends RuntimeException { + public NotFoundException(){ + super(); + } + public NotFoundException(String message) { + super(message); + } + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index e999f92730..5d14537fe7 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -4,6 +4,7 @@ import guru.springframework.converters.RecipeCommandToRecipe; import guru.springframework.converters.RecipeToRecipeCommand; import guru.springframework.domain.Recipe; +import guru.springframework.exceptions.NotFoundException; import guru.springframework.repositories.RecipeRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -39,9 +40,8 @@ public Recipe findById(Long l){ Optional recipeOptional = recipeRepository.findById(l); if(!recipeOptional.isPresent()){ - throw new RuntimeException("Recipe not found!"); + throw new NotFoundException("Recipe not found!"); } - return recipeOptional.get(); } @Override diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index 3bb912fe4b..73f451ca51 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -2,6 +2,7 @@ import guru.springframework.commands.RecipeCommand; import guru.springframework.domain.Recipe; +import guru.springframework.exceptions.NotFoundException; import guru.springframework.services.RecipeService; import org.junit.Before; import org.junit.Test; @@ -13,7 +14,6 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; @@ -44,7 +44,14 @@ public void testGetRecipe() throws Exception { .andExpect(MockMvcResultMatchers.view().name("recipe/show")) .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); } - + @Test + public void testGetRecipeNotFound() throws Exception { + Recipe recipe = new Recipe(); + recipe.setId(1L); + when(recipeService.findById(anyLong())).thenThrow(NotFoundException.class); + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/show")) + .andExpect(MockMvcResultMatchers.status().isNotFound()); + } @Test public void testGetNewRecipeForm() throws Exception { RecipeCommand command = new RecipeCommand(); diff --git a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java index 167719c876..bf1b716db8 100644 --- a/src/test/java/guru/springframework/services/RecipeServiceImplTest.java +++ b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java @@ -3,6 +3,7 @@ import guru.springframework.converters.RecipeCommandToRecipe; import guru.springframework.converters.RecipeToRecipeCommand; import guru.springframework.domain.Recipe; +import guru.springframework.exceptions.NotFoundException; import guru.springframework.repositories.RecipeRepository; import org.junit.Before; import org.junit.Test; @@ -48,7 +49,12 @@ public void getRecipeByIdTest() throws Exception { verify(recipeRepository,times(1)).findById(anyLong()); verify(recipeRepository,never()).findAll(); } - + @Test(expected = NotFoundException.class) + public void getRecipeByIdTestNotFound() throws Exception { + Optional recipeOptional = Optional.empty(); + when(recipeRepository.findById(anyLong())).thenReturn(recipeOptional); + Recipe recipeReturned = recipeService.findById(1L); + } @Test public void getRecipesTest() throws Exception { Recipe recipe = new Recipe(); From 82b7c1ce1bef00b38d4cdf730be276d68f8d2548 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 09:26:07 -0600 Subject: [PATCH 26/36] Recipe Not Found Exception Handling with 404 Not Found legend added --- .../controllers/RecipeController.java | 11 ++++++++ src/main/resources/templates/404error.html | 27 +++++++++++++++++++ .../controllers/RecipeControllerTest.java | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/templates/404error.html diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index 0699df50b2..0658ce3cd8 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -1,11 +1,14 @@ package guru.springframework.controllers; import guru.springframework.commands.RecipeCommand; +import guru.springframework.exceptions.NotFoundException; import guru.springframework.services.RecipeService; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; @Slf4j @Controller @@ -45,4 +48,12 @@ public String deleteById(@PathVariable String id){ recipeService.deleteById(Long.valueOf(id)); return "redirect:/"; } + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(NotFoundException.class) + public ModelAndView handleNotFound(){ + log.error("Handling not found exception"); + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName("404error"); + return modelAndView; + } } diff --git a/src/main/resources/templates/404error.html b/src/main/resources/templates/404error.html new file mode 100644 index 0000000000..715d581416 --- /dev/null +++ b/src/main/resources/templates/404error.html @@ -0,0 +1,27 @@ + + + + + Recipe Home + + + + + + + +
    +
    +
    +

    404 Not Found

    +
    +
    +
    + + \ No newline at end of file diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index 73f451ca51..4dda79ff06 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -50,7 +50,8 @@ public void testGetRecipeNotFound() throws Exception { recipe.setId(1L); when(recipeService.findById(anyLong())).thenThrow(NotFoundException.class); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/show")) - .andExpect(MockMvcResultMatchers.status().isNotFound()); + .andExpect(MockMvcResultMatchers.status().isNotFound()) + .andExpect(MockMvcResultMatchers.view().name("404error")); } @Test public void testGetNewRecipeForm() throws Exception { From 0d9c09b0d6141ee40663e4f2911ed2ccd800b7a7 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 10:07:47 -0600 Subject: [PATCH 27/36] Add not found id to error handler --- .../guru/springframework/controllers/RecipeController.java | 4 +++- .../java/guru/springframework/services/RecipeServiceImpl.java | 2 +- src/main/resources/templates/404error.html | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index 0658ce3cd8..a5212bf1a0 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -50,10 +50,12 @@ public String deleteById(@PathVariable String id){ } @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler(NotFoundException.class) - public ModelAndView handleNotFound(){ + public ModelAndView handleNotFound(Exception exception){ log.error("Handling not found exception"); + log.error(exception.getMessage()); ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("404error"); + modelAndView.addObject("exception",exception); return modelAndView; } } diff --git a/src/main/java/guru/springframework/services/RecipeServiceImpl.java b/src/main/java/guru/springframework/services/RecipeServiceImpl.java index 5d14537fe7..9f863a20f2 100644 --- a/src/main/java/guru/springframework/services/RecipeServiceImpl.java +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -40,7 +40,7 @@ public Recipe findById(Long l){ Optional recipeOptional = recipeRepository.findById(l); if(!recipeOptional.isPresent()){ - throw new NotFoundException("Recipe not found!"); + throw new NotFoundException("Recipe not found for ID value : " + l.toString()); } return recipeOptional.get(); } diff --git a/src/main/resources/templates/404error.html b/src/main/resources/templates/404error.html index 715d581416..dfa99c0635 100644 --- a/src/main/resources/templates/404error.html +++ b/src/main/resources/templates/404error.html @@ -20,6 +20,7 @@

    404 Not Found

    +

    From 52b24aaf651b148810414dc70764436e65400687 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 10:52:04 -0600 Subject: [PATCH 28/36] Add not found input string to error handler --- .../controllers/RecipeController.java | 10 +++++++ src/main/resources/templates/400error.html | 28 +++++++++++++++++++ .../controllers/RecipeControllerTest.java | 9 ++++-- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/templates/400error.html diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index a5212bf1a0..65a34570b7 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -58,4 +58,14 @@ public ModelAndView handleNotFound(Exception exception){ modelAndView.addObject("exception",exception); return modelAndView; } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(NumberFormatException.class) + public ModelAndView handleNumberFormat(Exception exception) { + log.error("Handling number format exception"); + log.error(exception.getMessage()); + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName("400error"); + modelAndView.addObject("exception",exception); + return modelAndView; + } } diff --git a/src/main/resources/templates/400error.html b/src/main/resources/templates/400error.html new file mode 100644 index 0000000000..33d8992904 --- /dev/null +++ b/src/main/resources/templates/400error.html @@ -0,0 +1,28 @@ + + + + + Recipe Home + + + + + + + +
    +
    +
    +

    400 Bad Request

    +

    +
    +
    +
    + + \ No newline at end of file diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index 4dda79ff06..be833cd052 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -46,14 +46,19 @@ public void testGetRecipe() throws Exception { } @Test public void testGetRecipeNotFound() throws Exception { - Recipe recipe = new Recipe(); - recipe.setId(1L); when(recipeService.findById(anyLong())).thenThrow(NotFoundException.class); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/show")) .andExpect(MockMvcResultMatchers.status().isNotFound()) .andExpect(MockMvcResultMatchers.view().name("404error")); } @Test + public void testNumberFormat() throws Exception { + when(recipeService.findById(anyLong())).thenThrow(NumberFormatException.class); + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/asdf/show")) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.view().name("400error")); + } + @Test public void testGetNewRecipeForm() throws Exception { RecipeCommand command = new RecipeCommand(); From a5cb96f917adf0e4428b7ddff6694729763c172b Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 11:48:23 -0600 Subject: [PATCH 29/36] Make Bad Request error global --- .../ControllerExceptionHandler.java | 23 +++++++++++++++++++ .../controllers/RecipeController.java | 10 -------- .../controllers/ImageControllerTest.java | 10 +++++++- .../controllers/RecipeControllerTest.java | 4 +++- 4 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 src/main/java/guru/springframework/controllers/ControllerExceptionHandler.java diff --git a/src/main/java/guru/springframework/controllers/ControllerExceptionHandler.java b/src/main/java/guru/springframework/controllers/ControllerExceptionHandler.java new file mode 100644 index 0000000000..3bbb33afda --- /dev/null +++ b/src/main/java/guru/springframework/controllers/ControllerExceptionHandler.java @@ -0,0 +1,23 @@ +package guru.springframework.controllers; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.ModelAndView; + +@Slf4j +@ControllerAdvice +public class ControllerExceptionHandler { + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(NumberFormatException.class) + public ModelAndView handleNumberFormat(Exception exception) { + log.error("Handling number format exception"); + log.error(exception.getMessage()); + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName("400error"); + modelAndView.addObject("exception",exception); + return modelAndView; + } +} diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index 65a34570b7..a5212bf1a0 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -58,14 +58,4 @@ public ModelAndView handleNotFound(Exception exception){ modelAndView.addObject("exception",exception); return modelAndView; } - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(NumberFormatException.class) - public ModelAndView handleNumberFormat(Exception exception) { - log.error("Handling number format exception"); - log.error(exception.getMessage()); - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("400error"); - modelAndView.addObject("exception",exception); - return modelAndView; - } } diff --git a/src/test/java/guru/springframework/controllers/ImageControllerTest.java b/src/test/java/guru/springframework/controllers/ImageControllerTest.java index 28a3a437ad..db46b2059a 100644 --- a/src/test/java/guru/springframework/controllers/ImageControllerTest.java +++ b/src/test/java/guru/springframework/controllers/ImageControllerTest.java @@ -32,7 +32,9 @@ public class ImageControllerTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); controller = new ImageController(imageService,recipeService); - mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + mockMvc = MockMvcBuilders.standaloneSetup(controller) + .setControllerAdvice(new ControllerExceptionHandler()) + .build(); } @Test public void getImageForm() throws Exception { @@ -82,4 +84,10 @@ public void renderImageFromDB() throws Exception { byte[] responseBytes = response.getContentAsByteArray(); assertEquals(s.getBytes().length,responseBytes.length); } + @Test + public void testGetImageNumberFormatException() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/recipe/asdf/recipeimage")) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andExpect(MockMvcResultMatchers.view().name("400error")); + } } diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index be833cd052..b405d82a30 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -29,7 +29,9 @@ public class RecipeControllerTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); recipeController = new RecipeController(recipeService); - mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build(); + mockMvc = MockMvcBuilders.standaloneSetup(recipeController) + .setControllerAdvice(new ControllerExceptionHandler()) + .build(); } @Test From e25cb3de8f632ebb7ac6dbc5d2f566e23930e335 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 12:27:56 -0600 Subject: [PATCH 30/36] Adding Constraints into the Recipe Command --- .../commands/RecipeCommand.java | 15 +++++ .../controllers/RecipeController.java | 15 ++++- .../controllers/RecipeControllerTest.java | 58 +++++++++++++------ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/main/java/guru/springframework/commands/RecipeCommand.java b/src/main/java/guru/springframework/commands/RecipeCommand.java index bd0971a14b..6558e41321 100644 --- a/src/main/java/guru/springframework/commands/RecipeCommand.java +++ b/src/main/java/guru/springframework/commands/RecipeCommand.java @@ -4,7 +4,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.hibernate.validator.constraints.URL; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; import java.util.HashSet; import java.util.Set; @@ -13,12 +18,22 @@ @NoArgsConstructor public class RecipeCommand { private Long id; + @NotBlank + @Size(min = 3, max = 255) private String description; + @Min(1) + @Max(999) private Integer prepTime; + @Min(1) + @Max(999) private Integer cookTime; + @Min(1) + @Max(100) private Integer servings; private String source; + @URL private String url; + @NotBlank private String directions; private Set ingredients = new HashSet<>(); private Byte[] image; diff --git a/src/main/java/guru/springframework/controllers/RecipeController.java b/src/main/java/guru/springframework/controllers/RecipeController.java index a5212bf1a0..92c8ac6fac 100644 --- a/src/main/java/guru/springframework/controllers/RecipeController.java +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -7,13 +7,17 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; +import javax.validation.Valid; + @Slf4j @Controller public class RecipeController { private final RecipeService recipeService; + private static final String RECIPE_RECIPEFORM_URL = "recipe/recipeform"; public RecipeController(RecipeService recipeService) { this.recipeService = recipeService; @@ -32,12 +36,17 @@ public String newRecipe(Model model){ @GetMapping("recipe/{id}/update") public String updateRecipe(@PathVariable String id, Model model){ model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(id))); - return "recipe/recipeform"; + return RECIPE_RECIPEFORM_URL; } @PostMapping("recipe") - public String serveOrUpdate(@ModelAttribute RecipeCommand command){ + public String saveOrUpdate(@Valid @ModelAttribute("recipe") RecipeCommand command, BindingResult bindingResult){ + if(bindingResult.hasErrors()) { + bindingResult.getAllErrors().forEach(objectError -> { + log.debug(objectError.toString()); + }); + return RECIPE_RECIPEFORM_URL; + } RecipeCommand savedCommand = recipeService.saveRecipeCommand(command); - return "redirect:/recipe/" + savedCommand.getId() + "/show"; } diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index b405d82a30..2219229924 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -17,6 +17,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; public class RecipeControllerTest { @Mock @@ -42,32 +44,32 @@ public void testGetRecipe() throws Exception { when(recipeService.findById(anyLong())).thenReturn(recipe); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/show")) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.view().name("recipe/show")) - .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + .andExpect(status().isOk()) + .andExpect(view().name("recipe/show")) + .andExpect(model().attributeExists("recipe")); } @Test public void testGetRecipeNotFound() throws Exception { when(recipeService.findById(anyLong())).thenThrow(NotFoundException.class); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/show")) - .andExpect(MockMvcResultMatchers.status().isNotFound()) - .andExpect(MockMvcResultMatchers.view().name("404error")); + .andExpect(status().isNotFound()) + .andExpect(view().name("404error")); } @Test public void testNumberFormat() throws Exception { when(recipeService.findById(anyLong())).thenThrow(NumberFormatException.class); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/asdf/show")) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.view().name("400error")); + .andExpect(status().isBadRequest()) + .andExpect(view().name("400error")); } @Test public void testGetNewRecipeForm() throws Exception { RecipeCommand command = new RecipeCommand(); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/new")) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.view().name("recipe/recipeform")) - .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + .andExpect(status().isOk()) + .andExpect(view().name("recipe/recipeform")) + .andExpect(model().attributeExists("recipe")); } @Test @@ -77,11 +79,31 @@ public void testPostNewRecipeForm() throws Exception { when(recipeService.saveRecipeCommand(any())).thenReturn(command); - mockMvc.perform(MockMvcRequestBuilders.post("/recipe").contentType(MediaType.APPLICATION_FORM_URLENCODED).param("id","").param("description","some string")) - .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) - .andExpect(MockMvcResultMatchers.view().name("redirect:/recipe/2/show")); + mockMvc.perform(post("/recipe") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("id", "") + .param("description", "some string") + .param("directions", "some directions") + ) + .andExpect(status().is3xxRedirection()) + .andExpect(view().name("redirect:/recipe/2/show")); } + @Test + public void testPostNewRecipeFormValidationFail() throws Exception { + RecipeCommand command = new RecipeCommand(); + command.setId(2L); + when(recipeService.saveRecipeCommand(any())).thenReturn(command); + + mockMvc.perform(post("/recipe") + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .param("id", "") + + ) + .andExpect(status().isOk()) + .andExpect(model().attributeExists("recipe")) + .andExpect(view().name("recipe/recipeform")); + } @Test public void testGetUpdateView() throws Exception { RecipeCommand command = new RecipeCommand(); @@ -90,16 +112,16 @@ public void testGetUpdateView() throws Exception { when(recipeService.findCommandById(anyLong())).thenReturn(command); mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/update")) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.view().name("recipe/recipeform")) - .andExpect(MockMvcResultMatchers.model().attributeExists("recipe")); + .andExpect(status().isOk()) + .andExpect(view().name("recipe/recipeform")) + .andExpect(model().attributeExists("recipe")); } @Test public void testDeleteAction() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/recipe/1/delete")) - .andExpect(MockMvcResultMatchers.status().is3xxRedirection()) - .andExpect(MockMvcResultMatchers.view().name("redirect:/")); + .andExpect(status().is3xxRedirection()) + .andExpect(view().name("redirect:/")); verify(recipeService,times(1)).deleteById(anyLong()); } From 7c1afdcccf7ccdf1dd8e7bd3cbeaa7e38bbd7e04 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 12:44:25 -0600 Subject: [PATCH 31/36] Updated form to visualize constraint errors --- .../templates/recipe/recipeform.html | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/main/resources/templates/recipe/recipeform.html b/src/main/resources/templates/recipe/recipeform.html index 6c156167d5..cf086f0eef 100644 --- a/src/main/resources/templates/recipe/recipeform.html +++ b/src/main/resources/templates/recipe/recipeform.html @@ -20,6 +20,10 @@
    +
    +

    Please correct Errors below

    +
    +
    @@ -28,9 +32,14 @@

    Edit Recipe Information

    -
    +
    - + + +
      +
    • +
    +
    @@ -53,13 +62,23 @@

    Edit Recipe Information

    -
    +
    - + + +
      +
    • +
    +
    -
    - - +
    + + + +
      +
    • +
    +
    @@ -77,17 +96,27 @@

    Edit Recipe Information

    -
    +
    - + + +
      +
    • +
    +
    -
    +
    - + + +
      +
    • +
    +
    From ac55b14e9160951995c88c61e8cca6cd62348741 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 12:59:55 -0600 Subject: [PATCH 32/36] Added customized messages for constraints --- src/main/resources/messages.properties | 13 +++++++++++++ .../controllers/RecipeControllerTest.java | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/messages.properties diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties new file mode 100644 index 0000000000..2575b8009c --- /dev/null +++ b/src/main/resources/messages.properties @@ -0,0 +1,13 @@ +#set name properties +recipe.description = Description + +#Validation messages +#Order of precedence +# 1 code.objectName.fieldName +# 2 code.fieldName +# 3 code.fieldType (Java data type) +# 4 code +NotBlank.recipe.description = Description cannot be blank +Size.recipe.description = {0} must be between {2} and {1} characters long +Max.recipe.cookTime = {0} must be less than {1} +URL.recipe.url = Please provide a valid URL \ No newline at end of file diff --git a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java index 2219229924..adab36d09f 100644 --- a/src/test/java/guru/springframework/controllers/RecipeControllerTest.java +++ b/src/test/java/guru/springframework/controllers/RecipeControllerTest.java @@ -98,7 +98,7 @@ public void testPostNewRecipeFormValidationFail() throws Exception { mockMvc.perform(post("/recipe") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .param("id", "") - + .param("cookTime","3000") ) .andExpect(status().isOk()) .andExpect(model().attributeExists("recipe")) From c2cf1c8b17a3a9b10e7c1b6c172319c690dc98dc Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Thu, 14 Dec 2023 13:16:51 -0600 Subject: [PATCH 33/36] Internationalization --- src/main/resources/messages.properties | 2 +- src/main/resources/templates/recipe/recipeform.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 2575b8009c..104a37c42f 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1,5 +1,5 @@ #set name properties -recipe.description = Description +recipe.description = Description (default) #Validation messages #Order of precedence diff --git a/src/main/resources/templates/recipe/recipeform.html b/src/main/resources/templates/recipe/recipeform.html index cf086f0eef..6ee4164580 100644 --- a/src/main/resources/templates/recipe/recipeform.html +++ b/src/main/resources/templates/recipe/recipeform.html @@ -33,7 +33,7 @@

    Edit Recipe Information

    - +
      From 72cd6bc8a23458ecbaa93a5e47ad035e8b77d2e5 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 22 Dec 2023 11:32:53 -0600 Subject: [PATCH 34/36] Configuration of MySQL --- pom.xml | 4 ++++ src/main/resources/application-dev.yml | 11 +++++++++ src/main/resources/application-prod.yml | 11 +++++++++ src/main/scripts/configure-mysql.sql | 31 +++++++++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application-prod.yml create mode 100644 src/main/scripts/configure-mysql.sql diff --git a/pom.xml b/pom.xml index 14cccaa1cc..9c97b95c89 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,10 @@ jquery 3.6.4 + + mysql + mysql-connector-java + org.springframework.boot spring-boot-starter-test diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000000..3996650cb4 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,11 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/sfg_dev + username: sfg_dev_user + password: guru + jpa: + hibernate: + ddl-auto: validate + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + database: mysql + show-sql: true \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000000..0d7c007b53 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,11 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/sfg_prod + username: sfg_prod_user + password: guru + jpa: + hibernate: + ddl-auto: validate + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + database: mysql + show-sql: false \ No newline at end of file diff --git a/src/main/scripts/configure-mysql.sql b/src/main/scripts/configure-mysql.sql new file mode 100644 index 0000000000..7d0748efad --- /dev/null +++ b/src/main/scripts/configure-mysql.sql @@ -0,0 +1,31 @@ +## Use to run mysql db docker image, optional if youre not using a local mysqldb +## docker run --name mysqldb -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -d mysql + +# connect to mysql and run as root user +#Create Databases +CREATE DATABASE sfg_dev; +CREATE DATABASE sfg_prod; + +#Create database service accounts +CREATE USER 'sfg_dev_user'@'localhost' IDENTIFIED BY 'guru'; +CREATE USER 'sfg_prod_user'@'localhost' IDENTIFIED BY 'guru'; +CREATE USER 'sfg_dev_user'@'%' IDENTIFIED BY 'guru'; +CREATE USER 'sfg_prod_user'@'%' IDENTIFIED BY 'guru'; + +#Database grants +GRANT SELECT ON sfg_dev.* to 'sfg_dev_user'@'localhost'; +GRANT INSERT ON sfg_dev.* to 'sfg_dev_user'@'localhost'; +GRANT DELETE ON sfg_dev.* to 'sfg_dev_user'@'localhost'; +GRANT UPDATE ON sfg_dev.* to 'sfg_dev_user'@'localhost'; +GRANT SELECT ON sfg_prod.* to 'sfg_prod_user'@'localhost'; +GRANT INSERT ON sfg_prod.* to 'sfg_prod_user'@'localhost'; +GRANT DELETE ON sfg_prod.* to 'sfg_prod_user'@'localhost'; +GRANT UPDATE ON sfg_prod.* to 'sfg_prod_user'@'localhost'; +GRANT SELECT ON sfg_dev.* to 'sfg_dev_user'@'%'; +GRANT INSERT ON sfg_dev.* to 'sfg_dev_user'@'%'; +GRANT DELETE ON sfg_dev.* to 'sfg_dev_user'@'%'; +GRANT UPDATE ON sfg_dev.* to 'sfg_dev_user'@'%'; +GRANT SELECT ON sfg_prod.* to 'sfg_prod_user'@'%'; +GRANT INSERT ON sfg_prod.* to 'sfg_prod_user'@'%'; +GRANT DELETE ON sfg_prod.* to 'sfg_prod_user'@'%'; +GRANT UPDATE ON sfg_prod.* to 'sfg_prod_user'@'%'; \ No newline at end of file From 0a1c73d7ca5df124633893c8e6cd1d2ff956bc12 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 22 Dec 2023 12:02:10 -0600 Subject: [PATCH 35/36] Database schema for use --- pom.xml | 10 ++++++++-- src/main/resources/application-dev.yml | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 9c97b95c89..5a590d82d1 100644 --- a/pom.xml +++ b/pom.xml @@ -63,8 +63,14 @@ 3.6.4 - mysql - mysql-connector-java + com.mysql + mysql-connector-j + 8.0.32 + + + javax.servlet + javax.servlet-api + 4.0.1 org.springframework.boot diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3996650cb4..b7425ab4e6 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,11 +1,20 @@ spring: datasource: - url: jdbc:mysql://localhost:3306/sfg_dev + url: jdbc:mysql://localhost:3306/sfg_dev ?serverTimezone=UTC username: sfg_dev_user - password: guru + password: Springboot23 + initialization-mode: always jpa: hibernate: ddl-auto: validate database-platform: org.hibernate.dialect.MySQL5InnoDBDialect database: mysql - show-sql: true \ No newline at end of file + show-sql: true +# properties: +# javax: +# persistence: +# schema-generation: +# create-source: metadata +# scripts: +# action: create +# create-target: guru_database_create.sql \ No newline at end of file From 08b356d3b2f949e277722cf37ba9b67fab63e1d7 Mon Sep 17 00:00:00 2001 From: bbqboneless Date: Fri, 22 Dec 2023 12:15:46 -0600 Subject: [PATCH 36/36] Database schema for use 2 --- .../bootstrap/BootStrapMySQL.java | 91 +++++++++++++++++++ .../bootstrap/RecipeBootstrap.java | 2 + .../resources/application-default.properties | 2 + src/main/resources/application-dev.yml | 1 + src/main/resources/application-prod.yml | 4 +- src/main/resources/{data.sql => data-h2.sql} | 0 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/main/java/guru/springframework/bootstrap/BootStrapMySQL.java create mode 100644 src/main/resources/application-default.properties rename src/main/resources/{data.sql => data-h2.sql} (100%) diff --git a/src/main/java/guru/springframework/bootstrap/BootStrapMySQL.java b/src/main/java/guru/springframework/bootstrap/BootStrapMySQL.java new file mode 100644 index 0000000000..f665b0ae91 --- /dev/null +++ b/src/main/java/guru/springframework/bootstrap/BootStrapMySQL.java @@ -0,0 +1,91 @@ +package guru.springframework.bootstrap; + +import guru.springframework.domain.Category; +import guru.springframework.domain.UnitOfMeasure; +import guru.springframework.repositories.CategoryRepository; +import guru.springframework.repositories.UnitOfMeasureRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Profile; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@Profile({"dev","prod"}) +public class BootStrapMySQL implements ApplicationListener { + private final CategoryRepository categoryRepository; + private final UnitOfMeasureRepository unitOfMeasureRepository; + + public BootStrapMySQL(CategoryRepository categoryRepository, + UnitOfMeasureRepository unitOfMeasureRepository) { + this.categoryRepository = categoryRepository; + this.unitOfMeasureRepository = unitOfMeasureRepository; + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { + + if (categoryRepository.count() == 0L){ + log.debug("Loading Categories"); + loadCategories(); + } + + if (unitOfMeasureRepository.count() == 0L){ + log.debug("Loading UOMs"); + loadUom(); + } + } + + private void loadCategories(){ + Category cat1 = new Category(); + cat1.setDescription("American"); + categoryRepository.save(cat1); + + Category cat2 = new Category(); + cat2.setDescription("Italian"); + categoryRepository.save(cat2); + + Category cat3 = new Category(); + cat3.setDescription("Mexican"); + categoryRepository.save(cat3); + + Category cat4 = new Category(); + cat4.setDescription("Fast Food"); + categoryRepository.save(cat4); + } + + private void loadUom(){ + UnitOfMeasure uom1 = new UnitOfMeasure(); + uom1.setDescription("Teaspoon"); + unitOfMeasureRepository.save(uom1); + + UnitOfMeasure uom2 = new UnitOfMeasure(); + uom2.setDescription("Tablespoon"); + unitOfMeasureRepository.save(uom2); + + UnitOfMeasure uom3 = new UnitOfMeasure(); + uom3.setDescription("Cup"); + unitOfMeasureRepository.save(uom3); + + UnitOfMeasure uom4 = new UnitOfMeasure(); + uom4.setDescription("Pinch"); + unitOfMeasureRepository.save(uom4); + + UnitOfMeasure uom5 = new UnitOfMeasure(); + uom5.setDescription("Ounce"); + unitOfMeasureRepository.save(uom5); + + UnitOfMeasure uom6 = new UnitOfMeasure(); + uom6.setDescription("Each"); + unitOfMeasureRepository.save(uom6); + + UnitOfMeasure uom7 = new UnitOfMeasure(); + uom7.setDescription("Pint"); + unitOfMeasureRepository.save(uom7); + + UnitOfMeasure uom8 = new UnitOfMeasure(); + uom8.setDescription("Dash"); + unitOfMeasureRepository.save(uom8); + } +} diff --git a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java index 7129f6af11..618b8f4404 100644 --- a/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java +++ b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.aspectj.weaver.ast.Not; import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Profile; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @@ -17,6 +18,7 @@ import java.util.Optional; @Slf4j @Component +@Profile("default") public class RecipeBootstrap implements ApplicationListener { private final CategoryRepository categoryRepository; private final RecipeRepository recipeRepository; diff --git a/src/main/resources/application-default.properties b/src/main/resources/application-default.properties new file mode 100644 index 0000000000..7fc0016312 --- /dev/null +++ b/src/main/resources/application-default.properties @@ -0,0 +1,2 @@ +spring.datasource.platform=h2 +spring.jpa.show-sql=true \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index b7425ab4e6..bc9ec1caf4 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -4,6 +4,7 @@ spring: username: sfg_dev_user password: Springboot23 initialization-mode: always + platform: mysql jpa: hibernate: ddl-auto: validate diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 0d7c007b53..c5d99188fd 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -2,7 +2,9 @@ spring: datasource: url: jdbc:mysql://localhost:3306/sfg_prod username: sfg_prod_user - password: guru + password: Springboot23 + initialization-mode: always + platform: mysql jpa: hibernate: ddl-auto: validate diff --git a/src/main/resources/data.sql b/src/main/resources/data-h2.sql similarity index 100% rename from src/main/resources/data.sql rename to src/main/resources/data-h2.sql