feat: basic Recipe class and RecipeController with GET, POST, PUT, DELETE
This commit is contained in:
parent
f8ad46e2c7
commit
3aa530ffde
7 changed files with 899 additions and 0 deletions
216
commons/src/main/java/commons/Recipe.java
Normal file
216
commons/src/main/java/commons/Recipe.java
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Copyright 2021 Delft University of Technology
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package commons;
|
||||
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OrderColumn;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Collection;
|
||||
|
||||
// TABLE named recipes
|
||||
@Entity
|
||||
@Table(name = "recipes")
|
||||
public class Recipe {
|
||||
|
||||
// PRIMARY Key, unique id for recipe, generated automatically.
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "id", nullable = false, unique = true)
|
||||
private Long id;
|
||||
|
||||
// Recipe name
|
||||
@Column(name = "name", nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Creates another table named recipe_ingredients which stores:
|
||||
* recipe_ingredients(recipe_id -> recipes(id), ingredient).
|
||||
* <p>
|
||||
* Example recipe_ingredients table:
|
||||
* <pre>
|
||||
* | recipe_id | ingredient |
|
||||
* |---------------------|------------|
|
||||
* | 0 (Chocolate Cake) | Egg |
|
||||
* | 0 (Chocolate Cake) | Egg |
|
||||
* | 0 (Chocolate Cake) | Flour |
|
||||
* | 1 (Steak) | 40g salt |
|
||||
* | 1 (Steak) | 40g pepper |
|
||||
* | 1 (Steak) | Meat |
|
||||
* |----------------------------------|
|
||||
* </pre>
|
||||
* TODO: Replace String with Embeddable Ingredient Class
|
||||
*/
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "recipe_ingredients", joinColumns = @JoinColumn(name = "recipe_id"))
|
||||
@Column(name = "ingredient")
|
||||
private List<String> ingredients = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates another table named recipe_preparation which stores:
|
||||
* recipe_preparation(recipe_id -> recipes(id), preparation_step, step_order).
|
||||
* <p>
|
||||
* Example recipe_preparation table:
|
||||
* <pre>
|
||||
* | recipe_id | preparation_step | step_order |
|
||||
* |---------------------|----------------------|------------|
|
||||
* | 0 (Chocolate Cake) | Preheat oven | 1 |
|
||||
* | 0 (Chocolate Cake) | Mix eggs and sugar | 2 |
|
||||
* | 0 (Chocolate Cake) | Add flour gradually | 3 |
|
||||
* | 1 (Steak) | Season meat | 1 |
|
||||
* | 1 (Steak) | Heat pan | 2 |
|
||||
* |---------------------------------------------------------|
|
||||
* </pre>
|
||||
*/
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "recipe_preparation", joinColumns = @JoinColumn(name = "recipe_id"))
|
||||
@Column(name = "preparation_step")
|
||||
@OrderColumn(name = "step_order")
|
||||
private List<String> preparationSteps = new ArrayList<>();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Recipe() {
|
||||
// for object mapper
|
||||
}
|
||||
|
||||
public Recipe(Long id, String name) {
|
||||
// Not used by JPA/Spring
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// TODO: Replace String with Embeddable Ingredient Class for ingredients
|
||||
public Recipe(Long id, String name, List<String> ingredients, List<String> preparationSteps) {
|
||||
// Not used by JPA/Spring
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.ingredients = ingredients;
|
||||
this.preparationSteps = preparationSteps;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable view of the ingredients list.
|
||||
* <p>
|
||||
* The returned list cannot be modified directly. To modify ingredients,
|
||||
* create a copy using {@link List#copyOf(Collection)} or create your own list,
|
||||
* populate it, and use {@link #setIngredients(List)} to update.
|
||||
*
|
||||
* @return An unmodifiable list of ingredients.
|
||||
* @see #setIngredients(List)
|
||||
*/
|
||||
// TODO: Replace String with Embeddable Ingredient Class
|
||||
public List<String> getIngredients() {
|
||||
// Disallow modifying the returned list.
|
||||
// You can still copy it with List.copyOf(...)
|
||||
return Collections.unmodifiableList(ingredients);
|
||||
}
|
||||
|
||||
|
||||
// TODO: Replace String with Embeddable Ingredient Class
|
||||
public void setIngredients(List<String> ingredients) {
|
||||
this.ingredients = ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable view of the preparation steps list.
|
||||
* <p>
|
||||
* The returned list cannot be modified directly. To modify preparation steps,
|
||||
* create a copy using {@link List#copyOf(Collection)} or create your own list,
|
||||
* populate it, and use {@link #setPreparationSteps(List)} to update.
|
||||
*
|
||||
* @return An unmodifiable list of preparation steps in order.
|
||||
* @see #setPreparationSteps(List)
|
||||
*/
|
||||
public List<String> getPreparationSteps() {
|
||||
// Disallow modifying the returned list.
|
||||
// You can still copy it with List.copyOf(...)
|
||||
return Collections.unmodifiableList(preparationSteps);
|
||||
}
|
||||
|
||||
public void setPreparationSteps(List<String> preparationSteps) {
|
||||
this.preparationSteps = preparationSteps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
// Faster/Better equals than reflectionEquals
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Recipe recipe = (Recipe) o;
|
||||
return Objects.equals(id, recipe.id); // Check only for ID, as its unique!
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id); // Use ID only, as its unique.
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Recipe{" +
|
||||
"name='" + name + '\'' +
|
||||
", ingredientsCount=" + ingredients.size() +
|
||||
", preparationStepsCount=" + preparationSteps.size() +
|
||||
"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a more detailed string than {@link #toString()} of this recipe, including
|
||||
* the full contents of all ingredients and preparation steps.
|
||||
* <p>
|
||||
* Intended only for debugging.
|
||||
*
|
||||
* @return A detailed string representation containing all recipe data.
|
||||
* @see #toString()
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public String toDetailedString() {
|
||||
return "Recipe{" +
|
||||
"name='" + name + '\'' +
|
||||
", ingredients=" + ingredients +
|
||||
", preparationSteps=" + preparationSteps +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
121
commons/src/test/java/commons/RecipeTest.java
Normal file
121
commons/src/test/java/commons/RecipeTest.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package commons;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class RecipeTest {
|
||||
|
||||
Recipe recipe;
|
||||
static final Long RECIPE_ID = 1L;
|
||||
|
||||
@BeforeEach
|
||||
void setupRecipe() {
|
||||
this.recipe = new Recipe(RECIPE_ID, "Chocolate Cake");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getId() {
|
||||
assertEquals(RECIPE_ID, recipe.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setId() {
|
||||
recipe.setId(RECIPE_ID + 1);
|
||||
assertEquals(RECIPE_ID + 1, recipe.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getName() {
|
||||
assertEquals("Chocolate Cake", recipe.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setName() {
|
||||
recipe.setName("Steak");
|
||||
assertEquals("Steak", recipe.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getIngredientsAddThrow() {
|
||||
// TODO: Change to actual Ingredient class later
|
||||
assertThrows(UnsupportedOperationException.class, () -> recipe.getIngredients().add("Lasagna"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getIngredientsClearThrow() {
|
||||
assertThrows(UnsupportedOperationException.class, () -> recipe.getIngredients().clear());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setIngredients() {
|
||||
// TODO: Change to actual Ingredient class later
|
||||
List<String> ingredients = new ArrayList<>(List.of("Chocolate", "Flour", "Egg"));
|
||||
recipe.setIngredients(ingredients);
|
||||
|
||||
assertEquals(recipe.getIngredients(), ingredients);
|
||||
assertEquals(recipe.getIngredients().size(), ingredients.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPreparationStepsAddThrow() {
|
||||
assertThrows(UnsupportedOperationException.class, () -> recipe.getPreparationSteps().add("Preheat Oven"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPreparationStepsClearThrow() {
|
||||
assertThrows(UnsupportedOperationException.class, () -> recipe.getPreparationSteps().clear());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setPreparationSteps() {
|
||||
List<String> steps = new ArrayList<>(List.of("Preheat oven", "Mix stuff", "decorate"));
|
||||
recipe.setPreparationSteps(steps);
|
||||
|
||||
assertEquals(recipe.getPreparationSteps(), steps);
|
||||
assertEquals(recipe.getPreparationSteps().size(), steps.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsSame() {
|
||||
Recipe recipe2 = new Recipe(RECIPE_ID, "Chocolate Cake");
|
||||
assertEquals(recipe, recipe2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsSameExceptId() {
|
||||
Recipe recipe2 = new Recipe(RECIPE_ID + 1, "Chocolate Cake");
|
||||
assertNotEquals(recipe, recipe2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsOnlySameId() {
|
||||
Recipe recipe2 = new Recipe(RECIPE_ID, "Some random recipe");
|
||||
assertEquals(recipe, recipe2); // Equals, we only look at ID!!!
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHashCodeSame() {
|
||||
Recipe recipe2 = new Recipe(RECIPE_ID, "Chocolate Cake");
|
||||
|
||||
assertEquals(recipe.hashCode(), recipe2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHashCodeSameExceptId() {
|
||||
Recipe recipe2 = new Recipe(RECIPE_ID + 1, "Chocolate Cake");
|
||||
|
||||
assertNotEquals(recipe.hashCode(), recipe2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHashCodeOnlySameId() {
|
||||
Recipe recipe2 = new Recipe(RECIPE_ID, "Some random recipe");
|
||||
|
||||
assertEquals(recipe.hashCode(), recipe2.hashCode()); // Same, only looks at ID!!!
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue