implement update recipe - does not create new ingredient groups, incredients or instruction steps yet

This commit is contained in:
Anika Raemer 2025-09-28 20:28:53 +02:00
parent 21742e3b24
commit e33dfdb845
3 changed files with 152 additions and 1 deletions

View file

@ -0,0 +1,117 @@
meta {
name: updateRecipe
type: http
seq: 7
}
put {
url: http://localhost:4000/recipe/44a8f38c-9387-439e-aed6-c3369b776b1c
body: json
auth: bearer
}
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImE0NDdlNDM0LTQyMWYtNDJiYS04MGRlLTM0ZDE1YzJmNWE2YyIsImlhdCI6MTc1OTA3NjA2NCwiZXhwIjoxNzU5MTYyNDY0fQ.exf_3fCrarW0LhUqPuadvp89BOUazEXtdSTkGDIAU_Q
}
body:json {
{
"id": "44a8f38c-9387-439e-aed6-c3369b776b1c",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"title": "Apfelkuchen Edeltrud",
"amount": 1,
"amountDescription": "Kuchen (26cm Durchmesser)",
"instructions": [
{
"id": "9042d658-0102-4e63-8637-a82c5653aa9d",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"text": "Mürbteig von 400 g Mehl herstellen",
"sortOrder": 1
},
{
"id": "42f834f1-54d1-4149-ad2e-e6565add719b",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"text": "Äpfel schälen und kleinschneiden.",
"sortOrder": 2
},
{
"id": "a45ad765-f775-4969-ad36-ca2d5645a2fc",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"text": "Restlichen Teig ausrollen, Kuchen damit abdecken und mit Milch bepinseln.",
"sortOrder": 4
},
{
"id": "a45ad765-f775-4969-ad36-ca2d5645a2fc",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"text": "Springform fetten, zwei Drittel des Teigs hineindrücken, Äpfel mit Rosinen vermischen und einfüllen.",
"sortOrder": 3
},
{
"id": "e0435853-b1b9-46cb-b53f-f5345ffca729",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"text": "Backen",
"sortOrder": 5
}
],
"ingredientGroups": [
{
"id": "680b885a-6670-4982-b257-336a0b4c09ee",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"title": "Für den Teig",
"sortOrder": 1,
"ingredients": [
{
"id": "d216f7f5-3e16-46f2-914f-af012ded9623",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"amount": 400,
"name": "Mehl",
"unit": "g",
"sortOrder": 1,
"subtext": null
}
]
},
{
"id": "ae6d32f4-a383-436b-8b9e-f693d74c955e",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"title": "Für die Füllung",
"sortOrder": 2,
"ingredients": [
{
"id": "9221faa8-a923-4c97-bb92-6d2dd7e94afc",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"amount": 5,
"name": "große Äpfel",
"unit": null,
"sortOrder": 1,
"subtext": null
},
{
"id": "7f3264d3-a002-40a7-92e2-4b3d02ea8bfd",
"createdAt": "2025-09-28T10:24:05.429Z",
"updatedAt": "2025-09-28T10:24:05.429Z",
"amount": 100,
"name": "Rosinen",
"unit": "g",
"sortOrder": 2,
"subtext": null
}
]
}
]
}
}
settings {
encodeUrl: true
}

View file

@ -5,6 +5,7 @@ import { NotFoundError, ValidationError } from "../errors/httpErrors.js";
import { RecipeInstructionStepDto } from "../dtos/RecipeInstructionStepDto.js";
import { RecipeIngredientGroupDto } from "../dtos/RecipeIngredientGroupDto.js";
import { RecipeIngredientDto } from "../dtos/RecipeIngredientDto.js";
import { Entity } from "typeorm";
/**
* Controls all recipe specific actions
@ -15,6 +16,11 @@ export class RecipeController {
private mapper: RecipeDtoEntityMapper
) { }
/**
* Load a specific recipe
* @param id recipe id
* @returns RecipeDto for requested recipe
*/
async getRecipeById(id: string){
const recipeEntity = await this.recipeRepository.findById(id);
if(recipeEntity === null){
@ -23,6 +29,20 @@ export class RecipeController {
const recipeDto = this.mapper.toDto(recipeEntity);
return recipeDto;
}
/**
* Update recipe data
* @param RecipeDto containing the entire updated recipe
* @returns Up-to-date RecipeDto as saved in the database
*/
async updateRecipe(dto: RecipeDto){
if (!this.isRecipeDtoValid(dto)) {
throw new ValidationError("recipe data is not valid!")
}
const recipeEntity = this.mapper.toEntity(dto);
// @todo doesn't create new ingredient groups, ingredients or instruction steps yet
const savedEntity = await this.recipeRepository.save(recipeEntity);
return this.mapper.toDto(savedEntity);
}
/**
* Create a new recipe
* @param dto RecipeDto containing the new recipe

View file

@ -7,6 +7,7 @@ import { RecipeDto } from "../dtos/RecipeDto.js";
import { RecipeIngredientDtoEntityMapper } from "../mappers/RecipeIngredientDtoEntityMapper.js";
import { RecipeIngredientGroupDtoEntityMapper } from "../mappers/RecipeIngredientGroupDtoEntityMapper.js";
import { RecipeInstructionStepDtoEntityMapper } from "../mappers/RecipeInstructionStepDtoEntityMapper.js";
import { ValidationError } from "../errors/httpErrors.js";
/**
* Handles all recipe related routes
@ -40,6 +41,19 @@ router.get(
const responseDto = await recipeController.getRecipeById(id);
res.status(201).json(responseDto);
})
)
);
router.put(
"/:id",
asyncHandler(async(req, res) =>{
const id = req.params.id;
const recipeDto : RecipeDto = req.body;
if(id != recipeDto.id){
throw new ValidationError("Cannot save recipe! ID in request body " + recipeDto.id + " doesn't match ID in path " + id +"!")
}
const responseDto = await recipeController.updateRecipe(recipeDto);
res.status(201).json(responseDto);
})
);
export default router;