Fall 2025 re
This document is an exam assignment for 3rd semester Datamatiker students at EK. After submission, the code in the main branch must no longer be changed.
- Allowed resources: written materials, personal computers, laptops, extra monitors, and internet resources. Headphones, and listening to music.
- Prohibited: communication with anyone. So no use of social media, forums, emails, SMS, chatrooms, etc.
- Do not store solutions on external networks or drives/hosts like Facebook, OneDrive, Google Drive, etc. And don’t share your code on Github until the end of the exam.
- Duration: 5 hours.
Upload a document (.txt or .pdf) to Wiseflow with:
- A link to your public GitHub repository. Remember to push your solutions at the very end of the exam. If you push it before that, then make sure the repository is private until hand-in. Do not copy the clone link from GitHub, but grab the link from the browser address bar and paste it into a document and upload to Wiseflow.
The Recipe Planner Application is a backend system for a cooking and meal-prep platform helping users manage recipes and ingredients. Users can browse, create, update, and delete recipes and manage their ingredients, and view their nutritional insights.
The system exposes REST endpoints via Javalin, uses JPA/Hibernate for persistence, integrates with an external Nutrition Stats API, and includes JWT-based authentication and REST testing.
| Category | Requirement |
|---|---|
| Architecture & Technology | The backend must be built using Java, Javalin, and JPA/Hibernate. |
| Data Persistence | Entities and DAOs must use JPA annotations and connect through a Hibernate configuration. |
| DTO Usage | All REST communication must use DTOs to decouple internal entities from exposed data. |
| API Design | The system must expose RESTful endpoints that conform to standard HTTP methods (GET, POST, PUT, DELETE). |
| Error Handling and Validation | All exceptions must be returned as structured JSON objects with appropriate HTTP status codes. Validation of input must be demonstrated and return meaningful error messages. |
| External Integration | The system must consume an external Nutrition Stats API to enrich recipes’ ingredients with nutritional data. |
| Testing | Each critical REST endpoint must be tested using JUnit and Rest Assured, including data setup and teardown. |
| Security | The system must implement JWT authentication, support role-based access control, and secure all write endpoints (POST, PUT, DELETE) with JWT and roles. |
| Documentation | The system must include a clear README.md with a status overview of how far you have come, and notes about any important design decisions. |
| Maintainability | Code should be modular, with a clean separation between layers (Controller, DAO, DTO, Entity, Routes). |
| Reliability | Unit and integration tests must be performed for all critical business logic and API endpoints. |
| Data Integrity | Deleting a recipe or ingredient must handle cascading relationships correctly and safely. |
I want to configure the database connection and entity management using Hibernate
so that the application can persist and retrieve recipe and ingredient data reliably.
- Each recipe has a title, description, and category (enum).
- Each ingredient has a
name,type(enum), anddescription. It also has a uniqueslug(String) used for matching with the external Nutrition Stats API. - Each recipe can have many ingredients, and each ingredient can belong to many recipes.
- The relationship between recipes and ingredients is many-to-many, implemented via a join table
RecipeIngredient. - The join table should include additional fields:
quantity(an integer),unit(String), andpreparation(e.g., “chopped”, “diced”). In this way you would be able to specify how much of each ingredient is needed for a recipe and how it should be prepared. Like so: 2 dl of milk, chopped onions, etc.
Hint! Because the join table has additional fields, you will need to create a separate entity for it (i.e., RecipeIngredient), and use @ManyToOne relationships from RecipeIngredient to both Recipe and Ingredient.
Acceptance Criteria
- The system must include entities for
Recipe,Ingredient, and the join tableRecipeIngredient. - The system must initialize with sample data via a Populator class that is run once at application startup.
Suggested Recipe Categories (enum) and examples:
| Enum value | Description |
|---|---|
| BREAKFAST | Morning meals |
| LUNCH | Midday meals |
| DINNER | Main meals |
| DESSERT | Sweet dishes |
| SNACK | Small/light meals |
Suggested Ingredient Types (enum) and examples:
| Enum value | Example ingredients | Description |
|---|---|---|
| VEGETABLE | Onion, Garlic, Carrot, Spinach | Plant-based edible vegetables |
| OIL | Olive Oil | Cooking oils and fats derived from plants |
| DAIRY | Butter, Milk | Milk-based products |
| PROTEIN | Egg, Chicken Breast, Beef | High-protein animal-based foods |
| SEASONING | Salt, Black Pepper, Soy Sauce | Ingredients used primarily for flavoring |
| GRAIN | Rice, Pasta | Grain-based staple foods |
| SWEETENER | Sugar, Honey | Ingredients used to add sweetness |
| FRUIT | Lemon, Lime | Edible fruits with sweet or sour flavors |
| HERB | Basil, Cilantro, Parsley | Fresh green herbs used for aroma and flavor |
| CONDIMENT | Tomato Sauce | Sauces or toppings added to enhance dishes |
| BAKING | Yeast, Baking Powder | Ingredients used for baking and dough preparation |
The Populator must create at least:
- 2–3 recipes
- 4–6 ingredients
- At least 1 recipe with linked ingredients via
RecipeIngredient
In case you need inspiration for samlple recipe data, a few are listed below in Appendix A .
I want to create, read, update, and delete recipe and ingredient records through DAOs
so that I can manage data consistently across the application.
Acceptance Criteria
RecipeDAOimplements CRUD and allows linking recipes and ingredients.IngredientDAOimplements basic CRUD as needed.- DTOs are used for all data exchange between layers.
I want to manage recipes using HTTP endpoints
so that I can perform standard CRUD operations and attach ingredients to recipes.
Note: You are not required to implement separate REST endpoints for ingredients. Ingredients can be created via the Populator or directly in the database.
Recipe CRUD
GET /recipes. Returns all recipes.GET /recipes/{id}. Returns a recipe including:- its base fields (title, description, category)
- all attached ingredients with their
quantity,unit, andpreparation
POST /recipes. Creates a new recipe.PUT /recipes/{id}. Updates an existing recipe.DELETE /recipes/{id}. Deletes a recipe and its associated join entities (RecipeIngredient).
POST /recipes/{recipeId}/ingredients. Creates aRecipeIngrediententry for the given recipe.Request body:
{ "ingredientId": 5, "quantity": 2, "unit": "dl", "preparation": "finely chopped" }The endpoint must:
- validate that the recipe exists
- validate that the ingredient exists
- Handling duplicate ingredient links is not required. You may assume that each ingredient is added to a recipe only once.
- create a
RecipeIngrediententity - store
quantity,unit, andpreparation - attach it to the correct recipe
Return value
- the created
RecipeIngredientDTO. Returning the updatedRecipeDTOis also acceptable.
I want to view and filter recipes by recipe category
so that I can find recipes that match specific meal types.
Acceptance Criteria
GET /recipes?category={category}filters recipes based on their category using JPA or streams.
I want to see nutrition insights (calories, protein, fat, carbs) for each recipe’s ingredients
so that I can assess the nutritional value of each recipe.
Acceptance Criteria:
- When retrieving a recipe by ID, the response must include enriched ingredient data fetched from the external Nutrition Stats API.
- Each ingredient in the response must include:
calories,protein,fat, andcarbs. - If the recipe has no ingredients, enrichment returns an empty list.
- If an ingredient is unknown in the external API, it is returned without enrichment data.
The external API is available at
https://apiprovider.cphbusinessapps.dk/api/v1/ingredients/nutrition?slugs=onion,garlic,olive-oil
where multiple ingredient slugs can be passed as a comma-separated list in theslugsquery parameter.The Ingredient entity has a
slugfield (e.g., “onion”, “garlic”, “olive-oil”) used to match with the external API.The API returns a JSON object with nutrition data for each ingredient in this format:
[
{
"id": "4a",
"slug": "onion",
"name": "Onion",
"categoryKey": "vegetable",
"calories": 40,
"protein": 1.1,
"fat": 0.1,
"carbs": 9.3,
"updatedAt": "2025-10-01T10:15:00.000Z"
},
{
"id": "5a",
"slug": "garlic",
"name": "Garlic",
"categoryKey": "vegetable",
"calories": 149,
"protein": 6.4,
"fat": 0.5,
"carbs": 33,
"updatedAt": "2025-10-01T10:15:00.000Z"
},
{
"id": "6a",
"slug": "olive-oil",
"name": "Olive Oil",
"categoryKey": "oil",
"calories": 884,
"protein": 0,
"fat": 100,
"carbs": 0,
"updatedAt": "2025-10-01T10:15:00.000Z"
}
]
Hint: The external API can be called once per recipe, passing multiple slug parameters.
NB: Each ingredient also has a slug, a short, unique identifier (e.g. “garlic”), used to match local ingredients with data from the external API.
NB: The date format for updatedAt is ZonedDateTime format, e.g. 2025-10-01T10:15:00.000Z.
Jackson might need an extra dependency to handle this format and this custom configuration of the ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
I want automated tests for all critical REST endpoints so that the application’s functionality is verified and regressions are avoided.
Acceptance Criteria:
- The most important success and failure paths are tested (not necessarily every tiny endpoint)
- Tests set up mock data and verify JSON responses and status codes.
- Recipe-by-ID tests confirm enrichment data is included.
I want to log in and access protected endpoints using a JWT token so that only authorized users can modify sensitive data.
Acceptance Criteria:
POST /loginauthenticates and returns a JWT.POST /registercreates a user with username, password, and role.- Secure all write endpoints (POST, PUT, DELETE) with JWT and roles.
- Protected endpoints validate the token and enforce roles.
- Unauthorized requests return
401 Unauthorized. - Tests verify secure access behavior.
| Dimension | Points | What to consider |
|---|---|---|
| REST design & correctness | 20 | Endpoints, Controller, status codes, DTO shapes |
| Data model & JPA mapping | 20 | Relations, cascading, constraints |
| Nutrition Stats integration | 15 | Correct call, enrichment logic, inclusion in responses |
| Security (JWT + roles) | 10 | Add roles to endpoints, adjust REST Assured tests to JWT |
| Testing | 20 | Coverage of success & failure paths, isolation |
| Error handling & validation | 10 | Exception handling, Consistent JSON errors, field errors |
| Code quality & README | 5 | Structure, clarity, how-to-run |
| Total | 100 |
- 2 eggs — beaten
- 10 g butter — melted in the pan
- 1 clove garlic — finely chopped
- 1 pinch salt — to taste
- 150 g rice — cooked
- 1 tbsp olive oil — heated in the pan
- ½ lemon — juice + a little zest
- 1 pinch salt — to taste
- 200 g pasta — cooked al dente
- 20 g butter — melted
- 5 g fresh basil — roughly torn
- 1 pinch black pepper — freshly ground