diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..cfd023b182 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,52 @@ +# Java Maven CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-java/ for more details +# +version: 2 +jobs: + build: + docker: + # specify the version you desire here + - image: circleci/openjdk:8-jdk + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/postgres:9.4 + + working_directory: ~/repo + + environment: + # Customize the JVM maximum heap limit + MAVEN_OPTS: -Xmx3200m + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum "pom.xml" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: mvn dependency:go-offline + + - save_cache: + paths: + - ~/.m2 + key: v1-dependencies-{{ checksum "pom.xml" }} + + # run tests! and gen code coverage + - run: mvn integration-test cobertura:cobertura + + - store_test_results: + path: target/surefire-reports + + - run: + name: Send to CodeCov + command: bash <(curl -s https://codecov.io/bash) + + + + diff --git a/README.md b/README.md index 0f4f568095..98c90d1d8f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ # Spring Boot Recipe Application -[![CircleCI](https://circleci.com/gh/springframeworkguru/spring5-recipe-app.svg?style=svg)](https://circleci.com/gh/springframeworkguru/spring5-recipe-app) -This repository is for an example application built in my Spring Framework 5 - Beginner to Guru - -You can learn about my Spring Framework 5 Online course [here.](https://go.springframework.guru/spring-framework-5-online-course) \ No newline at end of file +just to check what circleCI would say about this. diff --git a/pom.xml b/pom.xml index 25bb466d7f..ebc0883975 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,22 @@ h2 runtime + + org.projectlombok + lombok + 1.18.24 + + + + org.webjars + bootstrap + 3.3.7-1 + + + org.webjars + jquery + 3.2.1 + org.springframework.boot spring-boot-starter-test @@ -61,6 +77,47 @@ org.springframework.boot spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M5 + + ${project.build.outputDirectory} + + **/*IT.java + + + + + + integration-test + verify + + + + 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..91f6c31a85 --- /dev/null +++ b/src/main/java/guru/springframework/bootstrap/RecipeBootstrap.java @@ -0,0 +1,216 @@ +package guru.springframework.bootstrap; + + +import guru.springframework.domain.*; +import guru.springframework.repositories.CategoryRepository; +import guru.springframework.repositories.RecipeRepository; +import guru.springframework.repositories.UnitOfMeasureRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.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; + 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; + } + + @Override + @Transactional + public void onApplicationEvent(ContextRefreshedEvent event) { + recipeRepository.saveAll(getRecipes()); + log.debug("Loading Bootstrap Data"); + } + + private List getRecipes() { + + List recipes = new ArrayList<>(2); + + //get UOMs + Optional eachUomOptional = unitOfMeasureRepository.findByDescription("Each"); + + if(!eachUomOptional.isPresent()){ + throw new RuntimeException("Expected UOM Not Found"); + } + + Optional tableSpoonUomOptional = unitOfMeasureRepository.findByDescription("Tablespoon"); + + if(!tableSpoonUomOptional.isPresent()){ + throw new RuntimeException("Expected UOM Not Found"); + } + + Optional teaSpoonUomOptional = unitOfMeasureRepository.findByDescription("Teaspoon"); + + if(!teaSpoonUomOptional.isPresent()){ + throw new RuntimeException("Expected UOM Not Found"); + } + + Optional dashUomOptional = unitOfMeasureRepository.findByDescription("Dash"); + + if(!dashUomOptional.isPresent()){ + throw new RuntimeException("Expected UOM Not Found"); + } + + Optional pintUomOptional = unitOfMeasureRepository.findByDescription("Pint"); + + if(!pintUomOptional.isPresent()){ + throw new RuntimeException("Expected UOM Not Found"); + } + + Optional cupsUomOptional = unitOfMeasureRepository.findByDescription("Cup"); + + if(!cupsUomOptional.isPresent()){ + throw new RuntimeException("Expected UOM Not Found"); + } + + //get optionals + UnitOfMeasure eachUom = eachUomOptional.get(); + UnitOfMeasure tableSpoonUom = tableSpoonUomOptional.get(); + UnitOfMeasure teapoonUom = tableSpoonUomOptional.get(); + UnitOfMeasure dashUom = dashUomOptional.get(); + UnitOfMeasure pintUom = dashUomOptional.get(); + UnitOfMeasure cupsUom = cupsUomOptional.get(); + + //get Categories + 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(); + + //Yummy Guac + Recipe guacRecipe = new Recipe(); + guacRecipe.setDescription("Perfect Guacamole"); + guacRecipe.setPrepTime(10); + guacRecipe.setCookTime(0); + guacRecipe.setDifficulty(Difficulty.EASY); + guacRecipe.setDirections("1 Cut avocado, remove flesh: Cut the avocados in half. Remove seed. Score the inside of the avocado with a blunt knife and scoop out the flesh with a spoon" + + "\n" + + "2 Mash with a fork: Using a fork, roughly mash the avocado. (Don't overdo it! The guacamole should be a little chunky.)" + + "\n" + + "3 Add salt, lime juice, and the rest: Sprinkle with salt and lime (or lemon) juice. The acid in the lime juice will provide some balance to the richness of the avocado and will help delay the avocados from turning brown.\n" + + "Add the chopped onion, cilantro, black pepper, and chiles. Chili peppers vary individually in their hotness. So, start with a half of one chili pepper and add to the guacamole to your desired degree of hotness.\n" + + "Remember that much of this is done to taste because of the variability in the fresh ingredients. Start with this recipe and adjust to your taste.\n" + + "4 Cover with plastic and chill to store: Place plastic wrap on the surface of the guacamole cover it and to prevent air reaching it. (The oxygen in the air causes oxidation which will turn the guacamole brown.) Refrigerate until ready to serve.\n" + + "Chilling tomatoes hurts their flavor, so if you want to add chopped tomato to your guacamole, add it just before serving.\n" + + "\n" + + "\n" + + "Read more: http://www.simplyrecipes.com/recipes/perfect_guacamole/#ixzz4jvpiV9Sd"); + + Notes guacNotes = new Notes(); + guacNotes.setRecipeNotes("For a very quick guacamole just take a 1/4 cup of salsa and mix it in with your mashed avocados.\n" + + "Feel free to experiment! One classic Mexican guacamole has pomegranate seeds and chunks of peaches in it (a Diana Kennedy favorite). Try guacamole with added pineapple, mango, or strawberries.\n" + + "The simplest version of guacamole is just mashed avocados with salt. Don't let the lack of availability of other ingredients stop you from making guacamole.\n" + + "To extend a limited supply of avocados, add either sour cream or cottage cheese to your guacamole dip. Purists may be horrified, but so what? It tastes great.\n" + + "\n" + + "\n" + + "Read more: http://www.simplyrecipes.com/recipes/perfect_guacamole/#ixzz4jvoun5ws"); + + guacRecipe.setNotes(guacNotes); + + //very redundent - could add helper method, and make this simpler + guacRecipe.addIngredient(new Ingredient("ripe avocados", new BigDecimal(2), eachUom)); + guacRecipe.addIngredient(new Ingredient("Kosher salt", new BigDecimal(".5"), teapoonUom)); + guacRecipe.addIngredient(new Ingredient("fresh lime juice or lemon juice", new BigDecimal(2), tableSpoonUom)); + guacRecipe.addIngredient(new Ingredient("minced red onion or thinly sliced green onion", new BigDecimal(2), tableSpoonUom)); + guacRecipe.addIngredient(new Ingredient("serrano chiles, stems and seeds removed, minced", 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, chopped", new BigDecimal(".5"), eachUom)); + + guacRecipe.getCategories().add(americanCategory); + guacRecipe.getCategories().add(mexicanCategory); + + guacRecipe.setUrl("http://www.simplyrecipes.com/recipes/perfect_guacamole/"); + guacRecipe.setServings(4); + guacRecipe.setSource("Simply Recipes"); + + //add to return list + recipes.add(guacRecipe); + + //Yummy Tacos + Recipe tacosRecipe = new Recipe(); + tacosRecipe.setDescription("Spicy Grilled Chicken Taco"); + tacosRecipe.setCookTime(9); + tacosRecipe.setPrepTime(20); + tacosRecipe.setDifficulty(Difficulty.MODERATE); + + tacosRecipe.setDirections("1 Prepare 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.\n" + + "Set aside to marinate while the grill heats and you prepare the rest of the toppings.\n" + + "\n" + + "\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 165F. Transfer to a plate and rest for 5 minutes.\n" + + "4 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.\n" + + "Wrap warmed tortillas in a tea towel to keep them warm until serving.\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" + + "\n" + + "\n" + + "Read more: http://www.simplyrecipes.com/recipes/spicy_grilled_chicken_tacos/#ixzz4jvtrAnNm"); + + Notes tacoNotes = new Notes(); + tacoNotes.setRecipeNotes("We have a family motto and it is this: Everything goes better in a tortilla.\n" + + "Any and every kind of leftover can go inside a warm tortilla, usually with a healthy dose of pickled jalapenos. I can always sniff out a late-night snacker when the aroma of tortillas heating in a hot pan on the stove comes wafting through the house.\n" + + "Today’s tacos are more purposeful – a deliberate meal instead of a secretive midnight snack!\n" + + "First, I marinate the chicken briefly in a spicy paste of ancho chile powder, oregano, cumin, and sweet orange juice while the grill is heating. You can also use this time to prepare the taco toppings.\n" + + "Grill the chicken, then let it rest while you warm the tortillas. Now you are ready to assemble the tacos and dig in. The whole meal comes together in about 30 minutes!\n" + + "\n" + + "\n" + + "Read more: http://www.simplyrecipes.com/recipes/spicy_grilled_chicken_tacos/#ixzz4jvu7Q0MJ"); + + tacosRecipe.setNotes(tacoNotes); + + tacosRecipe.addIngredient(new Ingredient("Ancho Chili Powder", new BigDecimal(2), tableSpoonUom)); + tacosRecipe.addIngredient(new Ingredient("Dried Oregano", new BigDecimal(1), teapoonUom)); + tacosRecipe.addIngredient(new Ingredient("Dried Cumin", new BigDecimal(1), teapoonUom)); + tacosRecipe.addIngredient(new Ingredient("Sugar", new BigDecimal(1), teapoonUom)); + tacosRecipe.addIngredient(new Ingredient("Salt", new BigDecimal(".5"), teapoonUom)); + tacosRecipe.addIngredient(new Ingredient("Clove of Garlic, Choppedr", new BigDecimal(1), eachUom)); + tacosRecipe.addIngredient(new Ingredient("finely grated orange zestr", new BigDecimal(1), tableSpoonUom)); + tacosRecipe.addIngredient(new Ingredient("fresh-squeezed orange juice", new BigDecimal(3), tableSpoonUom)); + tacosRecipe.addIngredient(new Ingredient("Olive Oil", new BigDecimal(2), tableSpoonUom)); + tacosRecipe.addIngredient(new Ingredient("boneless chicken thighs", new BigDecimal(4), tableSpoonUom)); + tacosRecipe.addIngredient(new Ingredient("small corn tortillasr", new BigDecimal(8), eachUom)); + tacosRecipe.addIngredient(new Ingredient("packed baby arugula", new BigDecimal(3), cupsUom)); + tacosRecipe.addIngredient(new Ingredient("medium ripe avocados, slic", new BigDecimal(2), eachUom)); + tacosRecipe.addIngredient(new Ingredient("radishes, thinly sliced", new BigDecimal(4), eachUom)); + tacosRecipe.addIngredient(new Ingredient("cherry tomatoes, halved", new BigDecimal(".5"), pintUom)); + tacosRecipe.addIngredient(new Ingredient("red onion, thinly sliced", new BigDecimal(".25"), eachUom)); + tacosRecipe.addIngredient(new Ingredient("Roughly chopped cilantro", new BigDecimal(4), eachUom)); + tacosRecipe.addIngredient(new Ingredient("cup sour cream thinned with 1/4 cup milk", new BigDecimal(4), cupsUom)); + tacosRecipe.addIngredient(new Ingredient("lime, cut into wedges", new BigDecimal(4), eachUom)); + + tacosRecipe.getCategories().add(americanCategory); + tacosRecipe.getCategories().add(mexicanCategory); + + tacosRecipe.setUrl("http://www.simplyrecipes.com/recipes/spicy_grilled_chicken_tacos/"); + tacosRecipe.setServings(4); + tacosRecipe.setSource("Simply Recipes"); + + recipes.add(tacosRecipe); + return recipes; + } +} \ No newline at end of file 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..f437af9281 --- /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; +} \ No newline at end of file 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..60b397340d --- /dev/null +++ b/src/main/java/guru/springframework/commands/IngredientCommand.java @@ -0,0 +1,18 @@ +package guru.springframework.commands; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; + +@Setter +@Getter +@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/commands/NotesCommand.java b/src/main/java/guru/springframework/commands/NotesCommand.java new file mode 100644 index 0000000000..c3aa2cd6b5 --- /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; + +@Setter +@Getter +@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..b5bf55e9ba --- /dev/null +++ b/src/main/java/guru/springframework/commands/RecipeCommand.java @@ -0,0 +1,29 @@ +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 Byte[] image; + 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..b262121f61 --- /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; + +@Setter +@Getter +@NoArgsConstructor +public class UnitOfMeasureCommand { + private Long id; + private String description; +} diff --git a/src/main/java/guru/springframework/controllers/ImageController.java b/src/main/java/guru/springframework/controllers/ImageController.java new file mode 100644 index 0000000000..05e8942d90 --- /dev/null +++ b/src/main/java/guru/springframework/controllers/ImageController.java @@ -0,0 +1,64 @@ +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; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +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; + private final RecipeService recipeService; + + public ImageController(ImageService imageService, RecipeService recipeService) { + this.imageService = imageService; + this.recipeService = recipeService; + } + + @GetMapping("recipe/{id}/image") + public String showUploadForm(@PathVariable String id, Model model){ + model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(id))); + + return "recipe/imageuploadform"; + } + + @PostMapping("recipe/{id}/image") + public String handleImagePost(@PathVariable String id, @RequestParam("imagefile") MultipartFile file){ + + 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; //auto unboxing + } + + response.setContentType("image/jpeg"); + InputStream is = new ByteArrayInputStream(byteArray); + IOUtils.copy(is, response.getOutputStream()); + } + } +} \ No newline at end of file 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..26f3f604dc --- /dev/null +++ b/src/main/java/guru/springframework/controllers/IndexController.java @@ -0,0 +1,27 @@ +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.RequestMapping; + + +@Slf4j +@Controller +public class IndexController { + private final RecipeService recipeService; + + public IndexController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + @RequestMapping({"", "/", "/index"}) + public String getIndexPage(Model model) { + log.debug("Getting Index page"); + + model.addAttribute("recipes", recipeService.getRecipes()); + + return "index"; + } +} \ No newline at end of file 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..c8f57c3865 --- /dev/null +++ b/src/main/java/guru/springframework/controllers/IngredientController.java @@ -0,0 +1,97 @@ +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; +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.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + + +@Slf4j +@Controller +public class IngredientController { + + private final IngredientService ingredientService; + private final RecipeService recipeService; + private final UnitOfMeasureService unitOfMeasureService; + + public IngredientController(IngredientService ingredientService, RecipeService recipeService, UnitOfMeasureService unitOfMeasureService) { + this.ingredientService = ingredientService; + this.recipeService = recipeService; + this.unitOfMeasureService = unitOfMeasureService; + } + + @GetMapping("/recipe/{recipeId}/ingredients") + public String listIngredients(@PathVariable String recipeId, Model model){ + log.debug("Getting ingredient list for recipe id: " + recipeId); + + // use command object to avoid lazy load errors in Thymeleaf. + model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(recipeId))); + + return "recipe/ingredient/list"; + } + + @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("recipe/{recipeId}/ingredient/new") + public String newRecipe(@PathVariable String recipeId, Model model){ + + //make sure we have a good id value + RecipeCommand recipeCommand = recipeService.findCommandById(Long.valueOf(recipeId)); + //todo raise exception if null + + //need to return back parent id for hidden form property + 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("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 receipe id:" + savedCommand.getRecipeId()); + log.debug("saved ingredient id:" + savedCommand.getId()); + + return "redirect:/recipe/" + savedCommand.getRecipeId() + "/ingredient/" + savedCommand.getId() + "/show"; + } + + @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)); + + return "redirect:/recipe/" + recipeId + "/ingredients"; + } +} \ No newline at end of file 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..9a621d13a0 --- /dev/null +++ b/src/main/java/guru/springframework/controllers/RecipeController.java @@ -0,0 +1,59 @@ +package guru.springframework.controllers; + +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.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + + +@Slf4j +@Controller +public class RecipeController { + + private final RecipeService recipeService; + + public RecipeController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + @GetMapping("/recipe/{id}/show") + public String showById(@PathVariable String id, Model model){ + + model.addAttribute("recipe", recipeService.findById(Long.valueOf(id))); + return "recipe/show"; + } + + @GetMapping("recipe/new") + public String newRecipe(Model model){ + model.addAttribute("recipe", new RecipeCommand()); + + return "recipe/recipeform"; + } + + @GetMapping("recipe/{id}/update") + public String updateRecipe(@PathVariable String id, Model model){ + model.addAttribute("recipe", recipeService.findCommandById(Long.valueOf(id))); + return "recipe/recipeform"; + } + + @PostMapping("recipe") + public String saveOrUpdate(@ModelAttribute RecipeCommand command){ + RecipeCommand savedCommand = recipeService.saveRecipeCommand(command); + + return "redirect:/recipe/" + savedCommand.getId() + "/show"; + } + + @GetMapping("recipe/{id}/delete") + public String deleteById(@PathVariable String id){ + + log.debug("Deleting id: " + id); + + recipeService.deleteById(Long.valueOf(id)); + return "redirect:/"; + } +} \ No newline at end of file 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..5cdbceb9bb --- /dev/null +++ b/src/main/java/guru/springframework/converters/CategoryCommandToCategory.java @@ -0,0 +1,28 @@ +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; + } +} \ No newline at end of file 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..d00448a5a2 --- /dev/null +++ b/src/main/java/guru/springframework/converters/CategoryToCategoryCommand.java @@ -0,0 +1,29 @@ +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; + } +} \ No newline at end of file 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..6a577aef68 --- /dev/null +++ b/src/main/java/guru/springframework/converters/IngredientCommandToIngredient.java @@ -0,0 +1,42 @@ +package guru.springframework.converters; + +import guru.springframework.commands.IngredientCommand; +import guru.springframework.domain.Ingredient; +import guru.springframework.domain.Recipe; +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()); + + if(source.getRecipeId() != null){ + Recipe recipe = new Recipe(); + recipe.setId(source.getRecipeId()); + ingredient.setRecipe(recipe); + recipe.addIngredient(ingredient); + } + + ingredient.setAmount(source.getAmount()); + ingredient.setDescription(source.getDescription()); + ingredient.setUom(uomConverter.convert(source.getUom())); + return ingredient; + } +} \ No newline at end of file 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..0c2923cc99 --- /dev/null +++ b/src/main/java/guru/springframework/converters/IngredientToIngredientCommand.java @@ -0,0 +1,38 @@ +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()); + if (ingredient.getRecipe() != null) { + ingredientCommand.setRecipeId(ingredient.getRecipe().getId()); + } + ingredientCommand.setAmount(ingredient.getAmount()); + ingredientCommand.setDescription(ingredient.getDescription()); + ingredientCommand.setUom(uomConverter.convert(ingredient.getUom())); + return ingredientCommand; + } +} \ No newline at end of file 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..8af665784c --- /dev/null +++ b/src/main/java/guru/springframework/converters/NotesCommandToNotes.java @@ -0,0 +1,26 @@ +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; + } +} \ No newline at end of file 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..f67f987123 --- /dev/null +++ b/src/main/java/guru/springframework/converters/NotesToNotesCommand.java @@ -0,0 +1,26 @@ +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; + } +} \ No newline at end of file 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..c6b3d67df5 --- /dev/null +++ b/src/main/java/guru/springframework/converters/RecipeCommandToRecipe.java @@ -0,0 +1,56 @@ +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 categoryConveter; + private final IngredientCommandToIngredient ingredientConverter; + private final NotesCommandToNotes notesConverter; + + public RecipeCommandToRecipe(CategoryCommandToCategory categoryConveter, IngredientCommandToIngredient ingredientConverter, + NotesCommandToNotes notesConverter) { + this.categoryConveter = categoryConveter; + 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(categoryConveter.convert(category))); + } + + if (source.getIngredients() != null && source.getIngredients().size() > 0){ + source.getIngredients() + .forEach(ingredient -> recipe.getIngredients().add(ingredientConverter.convert(ingredient))); + } + + return recipe; + } +} \ No newline at end of file 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..ef043aa605 --- /dev/null +++ b/src/main/java/guru/springframework/converters/RecipeToRecipeCommand.java @@ -0,0 +1,59 @@ +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 categoryConveter; + private final IngredientToIngredientCommand ingredientConverter; + private final NotesToNotesCommand notesConverter; + + public RecipeToRecipeCommand(CategoryToCategoryCommand categoryConveter, IngredientToIngredientCommand ingredientConverter, + NotesToNotesCommand notesConverter) { + this.categoryConveter = categoryConveter; + 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.setImage(source.getImage()); + command.setNotes(notesConverter.convert(source.getNotes())); + + if (source.getCategories() != null && source.getCategories().size() > 0){ + source.getCategories() + .forEach((Category category) -> command.getCategories().add(categoryConveter.convert(category))); + } + + if (source.getIngredients() != null && source.getIngredients().size() > 0){ + source.getIngredients() + .forEach(ingredient -> command.getIngredients().add(ingredientConverter.convert(ingredient))); + } + + return command; + } +} \ No newline at end of file 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..2d0b8d7220 --- /dev/null +++ b/src/main/java/guru/springframework/converters/UnitOfMeasureCommandToUnitOfMeasure.java @@ -0,0 +1,26 @@ +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; + } +} \ No newline at end of file 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..85b59edacd --- /dev/null +++ b/src/main/java/guru/springframework/converters/UnitOfMeasureToUnitOfMeasureCommand.java @@ -0,0 +1,26 @@ +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; + } +} \ No newline at end of file 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..2ae72381d3 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Category.java @@ -0,0 +1,24 @@ +package guru.springframework.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.Set; + +@Getter +@Setter +@EqualsAndHashCode(exclude = {"recipes"}) +@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..0a87cbb97d --- /dev/null +++ b/src/main/java/guru/springframework/domain/Difficulty.java @@ -0,0 +1,6 @@ +package guru.springframework.domain; + +public enum Difficulty { + + EASY, MODERATE, KIND_OF_HARD, HARD +} \ No newline at end of file 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..1a47a31641 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Ingredient.java @@ -0,0 +1,43 @@ +package guru.springframework.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.math.BigDecimal; +@Getter +@Setter +@EqualsAndHashCode(exclude = {"recipe"}) +@Entity +public class Ingredient { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String description; + private BigDecimal amount; + + @OneToOne(fetch = FetchType.EAGER) + private UnitOfMeasure uom; + + @ManyToOne + private Recipe recipe; + + public Ingredient() { + } + + public Ingredient(String description, BigDecimal amount, UnitOfMeasure uom) { + this.description = description; + this.amount = amount; + this.uom = uom; + } + + public Ingredient(String description, BigDecimal amount, UnitOfMeasure uom, Recipe recipe) { + this.description = description; + this.amount = amount; + this.uom = uom; + this.recipe = recipe; + } + +} \ No newline at end of file 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..e1532b2116 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Notes.java @@ -0,0 +1,25 @@ +package guru.springframework.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; + +@Getter +@Setter +@EqualsAndHashCode(exclude = {"recipe"}) +@Entity +public class Notes { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + private Recipe recipe; + + @Lob + private String recipeNotes; + +} \ No newline at end of file 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..c92e928472 --- /dev/null +++ b/src/main/java/guru/springframework/domain/Recipe.java @@ -0,0 +1,58 @@ +package guru.springframework.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; +@Getter +@Setter +@Entity +public class Recipe { + + @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; + + @Lob + private String directions; + + @OneToMany(cascade = CascadeType.ALL, mappedBy = "recipe") + private Set ingredients = new HashSet<>(); + + @Lob + private Byte[] image; + + @Enumerated(value = EnumType.STRING) + private Difficulty difficulty; + + @OneToOne(cascade = CascadeType.ALL) + private Notes notes; + + @ManyToMany + @JoinTable(name = "recipe_category", + joinColumns = @JoinColumn(name = "recipe_id"), + inverseJoinColumns = @JoinColumn(name = "category_id")) + private Set categories = new HashSet<>(); + + public void setNotes(Notes notes) { + if (notes != null) { + this.notes = notes; + notes.setRecipe(this); + } + } + + public Recipe addIngredient(Ingredient ingredient){ + ingredient.setRecipe(this); + this.ingredients.add(ingredient); + return this; + } +} \ No newline at end of file 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..daff2c5aed --- /dev/null +++ b/src/main/java/guru/springframework/domain/UnitOfMeasure.java @@ -0,0 +1,23 @@ +package guru.springframework.domain; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Getter +@Setter +@Entity +public class UnitOfMeasure { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String description; +// @OneToOne //weArentdointthisin thisside because its unidirectional.//yehagnawbchanew miyakew relationship'un. +// private Ingredient ingredient; + +} 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..259336ceb9 --- /dev/null +++ b/src/main/java/guru/springframework/repositories/CategoryRepository.java @@ -0,0 +1,14 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.Category; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +//@Repository +public interface CategoryRepository extends CrudRepository { + + Optional findByDescription(String description); + +} + 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..db1af7ed32 --- /dev/null +++ b/src/main/java/guru/springframework/repositories/RecipeRepository.java @@ -0,0 +1,9 @@ +package guru.springframework.repositories; + +import guru.springframework.domain.Recipe; +import org.springframework.data.repository.CrudRepository; + +//@Repository//works without it // b/c CrudRepository extends Repository +public interface RecipeRepository extends CrudRepository { +/* Optional findByD(Long aLong);*/ +} 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..15cb9a4046 --- /dev/null +++ b/src/main/java/guru/springframework/repositories/UnitOfMeasureRepository.java @@ -0,0 +1,10 @@ +package guru.springframework.repositories; + +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/ImageService.java b/src/main/java/guru/springframework/services/ImageService.java new file mode 100644 index 0000000000..a8682c24b2 --- /dev/null +++ b/src/main/java/guru/springframework/services/ImageService.java @@ -0,0 +1,7 @@ +package guru.springframework.services; + +import org.springframework.web.multipart.MultipartFile; + +public interface ImageService { + void saveImageFile(Long recipeId, MultipartFile file); +} diff --git a/src/main/java/guru/springframework/services/ImageServiceImpl.java b/src/main/java/guru/springframework/services/ImageServiceImpl.java new file mode 100644 index 0000000000..49fb721ee0 --- /dev/null +++ b/src/main/java/guru/springframework/services/ImageServiceImpl.java @@ -0,0 +1,52 @@ +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.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +/** + * Created by jt on 7/3/17. + */ +@Slf4j +@Service +public class ImageServiceImpl implements ImageService { + + + private final RecipeRepository recipeRepository; + + public ImageServiceImpl( RecipeRepository recipeService) { + + this.recipeRepository = recipeService; + } + + @Override + @Transactional + public void saveImageFile(Long recipeId, MultipartFile 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) { + //todo handle better + log.error("Error occurred", e); + + e.printStackTrace(); + } + } +} \ No newline at end of file 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..c3239570ff --- /dev/null +++ b/src/main/java/guru/springframework/services/IngredientService.java @@ -0,0 +1,11 @@ +package guru.springframework.services; + +import guru.springframework.commands.IngredientCommand; + +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 new file mode 100644 index 0000000000..7ed3d71e99 --- /dev/null +++ b/src/main/java/guru/springframework/services/IngredientServiceImpl.java @@ -0,0 +1,141 @@ +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 org.springframework.transaction.annotation.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()){ + //todo impl error handling + 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()){ + //todo impl error handling + 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()){ + + //todo toss error if not found! + 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.setUom(unitOfMeasureRepository + .findById(command.getUom().getId()) + .orElseThrow(() -> new RuntimeException("UOM NOT FOUND"))); //todo address this + } else { + //add new Ingredient + Ingredient ingredient = ingredientCommandToIngredient.convert(command); + ingredient.setRecipe(recipe); + recipe.addIngredient(ingredient); + } + + Recipe savedRecipe = recipeRepository.save(recipe); + + Optional savedIngredientOptional = savedRecipe.getIngredients().stream() + .filter(recipeIngredients -> recipeIngredients.getId().equals(command.getId())) + .findFirst(); + + //check by description + if(!savedIngredientOptional.isPresent()){ + //not totally safe... But best guess + savedIngredientOptional = savedRecipe.getIngredients().stream() + .filter(recipeIngredients -> recipeIngredients.getDescription().equals(command.getDescription())) + .filter(recipeIngredients -> recipeIngredients.getAmount().equals(command.getAmount())) + .filter(recipeIngredients -> recipeIngredients.getUom().getId().equals(command.getUom().getId())) + .findFirst(); + } + + //to do check for fail + return ingredientToIngredientCommand.convert(savedIngredientOptional.get()); + } + + } + + @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); + } + } +} \ No newline at end of file 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..044b0b455c --- /dev/null +++ b/src/main/java/guru/springframework/services/RecipeService.java @@ -0,0 +1,15 @@ +package guru.springframework.services; + +import guru.springframework.commands.RecipeCommand; +import guru.springframework.domain.Recipe; + +import java.util.Set; + +public interface RecipeService { + Set getRecipes(); + 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 new file mode 100644 index 0000000000..4e28e4c8dc --- /dev/null +++ b/src/main/java/guru/springframework/services/RecipeServiceImpl.java @@ -0,0 +1,71 @@ +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.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +@Slf4j +@Service +public class RecipeServiceImpl implements RecipeService { + + private final RecipeRepository recipeRepository; + private final RecipeCommandToRecipe recipeCommandToRecipe; + private final RecipeToRecipeCommand recipeToRecipeCommand; + + public RecipeServiceImpl(RecipeRepository recipeRepository, RecipeCommandToRecipe recipeCommandToRecipe, RecipeToRecipeCommand recipeToRecipeCommand) { + this.recipeRepository = recipeRepository; + this.recipeCommandToRecipe = recipeCommandToRecipe; + this.recipeToRecipeCommand = recipeToRecipeCommand; + } + + @Override + public Set getRecipes() { + log.debug("I'm in the service"); + + Set recipeSet = new HashSet<>(); + 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(); + } + + @Override + @Transactional + public RecipeCommand findCommandById(Long l) { + return recipeToRecipeCommand.convert(findById(l)); + } + + @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); + } + + @Override + public void deleteById(Long idToDelete) { + recipeRepository.deleteById(idToDelete); + } +} \ No newline at end of file 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..1fabeb561c --- /dev/null +++ b/src/main/java/guru/springframework/services/UnitOfMeasureServiceImpl.java @@ -0,0 +1,34 @@ +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; + +/** + * Created by jt on 6/28/17. + */ +@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()); + } +} \ No newline at end of file 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/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000000..31108d219d --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,13 @@ +INSERT INTO category (description) VALUES ('American'); +INSERT INTO category (description) VALUES ('Italian'); +INSERT INTO category (description) VALUES ('Mexican'); +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/static/images/guacamole400x400.jpg b/src/main/resources/static/images/guacamole400x400.jpg new file mode 100644 index 0000000000..2362c15779 Binary files /dev/null and b/src/main/resources/static/images/guacamole400x400.jpg differ diff --git a/src/main/resources/static/images/j4ipv452.bmp b/src/main/resources/static/images/j4ipv452.bmp new file mode 100644 index 0000000000..3d14420817 Binary files /dev/null and b/src/main/resources/static/images/j4ipv452.bmp differ diff --git a/src/main/resources/static/images/kv2w7h4d.bmp b/src/main/resources/static/images/kv2w7h4d.bmp new file mode 100644 index 0000000000..c4a945206b Binary files /dev/null and b/src/main/resources/static/images/kv2w7h4d.bmp differ diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000000..527d379add --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,68 @@ + + + + + Recipe Home + + + + + + + + + + + + +
+
+
+
+ +
+

My Recipes!

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDDescriptionViewUpdateDelete
123Tasty Goodnees 1View
12333Tasty Goodnees 2View
334Tasty Goodnees 3ViewUpdateDelete
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/imageuploadform.html b/src/main/resources/templates/recipe/imageuploadform.html new file mode 100644 index 0000000000..2f8b2fcc56 --- /dev/null +++ b/src/main/resources/templates/recipe/imageuploadform.html @@ -0,0 +1,47 @@ + + + + + Image Upload Form + + + + + + + + + + + +
+
+
+
+
+
+

Upload a new recipe image

+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + \ No newline at end of file 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..6c6179fad3 --- /dev/null +++ b/src/main/resources/templates/recipe/ingredient/ingredientform.html @@ -0,0 +1,64 @@ + + + + + Edit Ingredient + + + + + + + + + + +
+
+
+ +
+ +
+
+
+

Edit Ingredient Information

+
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ +
+
+
+
+
+ + \ No newline at end of file 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..0fd2c95e7e --- /dev/null +++ b/src/main/resources/templates/recipe/ingredient/list.html @@ -0,0 +1,80 @@ + + + + + List Ingredients + + + + + + + + + + + + +
+
+
+
+ +
+
+
+

Ingredients

+
+
+ New +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDDescriptionViewUpdateDelete
123Tasty Goodnees 1ViewUpdateDelete
12333Tasty Goodnees 2ViewUpdateDelete
334Tasty Goodnees 3ViewUpdateDelete
+
+
+
+
+
+
+ + \ 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..2c2dd0cea7 --- /dev/null +++ b/src/main/resources/templates/recipe/ingredient/show.html @@ -0,0 +1,59 @@ + + + + + View Ingredient + + + + + + + + + + + +
+
+
+
+ +
+

Ingredient

+
+
+ +
+ + + + + + + + + + + + + + + + + + +
IDDescription
12333Tasty Goodnees 2ViewUpdateDelete
334Tasty Goodnees 3
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/recipeform.html b/src/main/resources/templates/recipe/recipeform.html new file mode 100644 index 0000000000..322b0a8218 --- /dev/null +++ b/src/main/resources/templates/recipe/recipeform.html @@ -0,0 +1,155 @@ + + + + + Recipe Form + + + + + + + + + + + +
+
+
+
+ +
+
+
+

Edit Recipe Information

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

Ingredients

+
+
+ View +
+
+
+
+
+
+
    +
  • 1 Cup of milk
  • +
  • 1 Teaspoon of chocolate
  • +
  • asdf
  • +
  • 1 Teaspoon of Sugar +
  • +
+
+
+
+
+
+
+

Directions

+
+
+
+
+
+
+
+
+
+
+

Notes

+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/show.html b/src/main/resources/templates/recipe/show.html new file mode 100644 index 0000000000..8693990833 --- /dev/null +++ b/src/main/resources/templates/recipe/show.html @@ -0,0 +1,183 @@ + + + + + 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:
+
+ +
+
+
+
+
+
+
+

Ingredients

+
+
+ View +
+
+
+
+
+
+
    +
  • 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.

+
+
+
+
+
+
+
+
+ + \ No newline at end of file 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..5b4af90bcc --- /dev/null +++ b/src/test/java/guru/springframework/controllers/IndexControllerTest.java @@ -0,0 +1,70 @@ +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.setup.MockMvcBuilders; +import org.springframework.ui.Model; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +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 testMockMVC() throws Exception { + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + + mockMvc.perform(get("/")) + .andExpect(status().isOk()) + .andExpect(view().name("index")); + } + + @Test + public void getIndexPage() throws Exception{ + //given + 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); + + //when + String viewName = controller.getIndexPage(model); + + //then + assertEquals("index", viewName); + verify(recipeService, times(1)).getRecipes(); + verify(model, times(1)).addAttribute(eq("recipes"), argumentCaptor.capture()); + Set setInController = argumentCaptor.getValue(); + assertEquals(2, setInController.size()); + } +} \ No newline at end of file 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..ef3c458e80 --- /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.assertEquals; + +public class CategoryTest { + + Category category; + @Before + public void setUp() { + category = new Category(); + } + + @Test + public void getId() { + Long idValue = new Long(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/repositories/UnitOfMeasureRepositoryIT.java b/src/test/java/guru/springframework/repositories/UnitOfMeasureRepositoryIT.java new file mode 100644 index 0000000000..d9d4d8edc5 --- /dev/null +++ b/src/test/java/guru/springframework/repositories/UnitOfMeasureRepositoryIT.java @@ -0,0 +1,38 @@ +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.assertEquals; + +@RunWith(SpringRunner.class) +@DataJpaTest//willbringembadedDatabase//also//willConfigureSpringdatajJPA +public class UnitOfMeasureRepositoryIT { + + @Autowired + UnitOfMeasureRepository unitOfMeasureRepository; + + @Before + public void setUp() throws Exception { + } + + @Test +// @DirtiesContext// ifwewantToreloadthecontextafterthistestMethod + public void findByDescription() throws Exception{ + Optional uomOptional = unitOfMeasureRepository.findByDescription("Teaspoon"); + assertEquals("Teaspoon", uomOptional.get().getDescription()); + } + + @Test + public void findByDescriptionCup() throws Exception{ + Optional uomOptional = unitOfMeasureRepository.findByDescription("Cup"); + assertEquals("Cup", 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 new file mode 100644 index 0000000000..3429e6447e --- /dev/null +++ b/src/test/java/guru/springframework/services/RecipeServiceImplTest.java @@ -0,0 +1,48 @@ +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; +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.assertEquals; +import static org.mockito.Mockito.*; + +public class RecipeServiceImplTest { + + RecipeServiceImpl recipeService; + @Mock + RecipeRepository recipeRepository; + @Mock + RecipeCommandToRecipe recipeCommandToRecipe; + @Mock + RecipeToRecipeCommand recipeToRecipeCommand; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this);//askingMockitoTogivemeMockOf:RecipeRepository// or anyotherpropertywith@Mock + + recipeService = new RecipeServiceImpl(recipeRepository,recipeCommandToRecipe,recipeToRecipeCommand); + } + + @Test + public void getRecipes() throws Exception { + Recipe recipe = new Recipe(); + HashSet recipesData = new HashSet(); + recipesData.add(recipe); + + when(recipeRepository.findAll()).thenReturn(recipesData); + + Set recipes = recipeService.getRecipes(); + + assertEquals(recipes.size(), 1); + verify(recipeRepository, times(1)).findAll(); + } +} \ No newline at end of file