From 58ef7fbc00c2ea5285dd60a9d7610bac28cef9ac Mon Sep 17 00:00:00 2001 From: Anika Raemer Date: Fri, 10 Oct 2025 18:04:59 +0200 Subject: [PATCH] add createOrUpdate for recipes --- src/endpoints/CompactRecipePoint.ts | 2 ++ src/endpoints/RecipePoint.ts | 19 +++++++++++++++++ src/handlers/CompactRecipeHandler.ts | 1 + src/handlers/RecipeHandler.ts | 32 ++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/endpoints/CompactRecipePoint.ts b/src/endpoints/CompactRecipePoint.ts index e7c6956..e75b509 100644 --- a/src/endpoints/CompactRecipePoint.ts +++ b/src/endpoints/CompactRecipePoint.ts @@ -16,6 +16,8 @@ const compactRecipeHandler = new CompactRecipeHandler(recipeRepository, compactR /** * Load header data of all recipes * Responds with a list of CompactRecipeDtos + * @todo request wrapper DTO + * @todo response wrapper DTO */ router.get( "/", diff --git a/src/endpoints/RecipePoint.ts b/src/endpoints/RecipePoint.ts index 3949add..f09bb3d 100644 --- a/src/endpoints/RecipePoint.ts +++ b/src/endpoints/RecipePoint.ts @@ -26,6 +26,7 @@ const recipeController = new RecipeHandler(recipeRepository, recipeMapper); * Create new recipe * Consumes: RecipeDto * Responds with RecipeDto + * DEPRECATED! */ router.post( "/", @@ -36,6 +37,23 @@ router.post( }) ); +/** + * Save or update recipe. + * Consumes: RecipeDto + * Responds with RecipeDto as saved in database + * A new recipe is created if the dto doesn't contain an ID. Otherwise, the + * existing recipe is updated. Throws an exception if the dto does contain an + * ID that's not present in the database + */ +router.post( + "/create-or-update", + asyncHandler(async(req, res) => { + const requestDto: RecipeDto = req.body; + const responseDto = await recipeController.createOrUpdateRecipe(requestDto); + res.status(201).json(responseDto); + }) +) + /** * Load recipe by id * Responds with RecipeDto @@ -54,6 +72,7 @@ router.get( * Also handles changes to instructions steps and ingredient (groups) * Consumes: RecipeDto * Responds with RecipeDto + * DEPRECATED */ router.put( "/:id", diff --git a/src/handlers/CompactRecipeHandler.ts b/src/handlers/CompactRecipeHandler.ts index 6e332e2..61482cd 100644 --- a/src/handlers/CompactRecipeHandler.ts +++ b/src/handlers/CompactRecipeHandler.ts @@ -31,6 +31,7 @@ export class CompactRecipeHandler { * * Recipe title must contain type string * @todo Full text search?? + */ async getMatchingRecipes(searchString : string){ if(!searchString || searchString.length===0){ diff --git a/src/handlers/RecipeHandler.ts b/src/handlers/RecipeHandler.ts index eee5eda..fd4df75 100644 --- a/src/handlers/RecipeHandler.ts +++ b/src/handlers/RecipeHandler.ts @@ -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 { RecipeEntity } from "../entities/RecipeEntity.js"; /** * Controls all recipe specific actions @@ -28,6 +29,37 @@ export class RecipeHandler { const recipeDto = this.mapper.toDto(recipeEntity); return recipeDto; } + + /** + * Save or update recipe depending on whether the DTO contains an ID + * @param dto recipe data + * @returns Recipe as saved in the database + */ + async createOrUpdateRecipe(dto: RecipeDto){ + if (!this.isRecipeDtoValid(dto)) { + throw new ValidationError("recipe data is not valid!") + } + var savedEntity: RecipeEntity; + const recipeId = dto.id + if(recipeId === undefined){ + // create new recipe + const recipeEntity = this.mapper.toEntity(dto) + savedEntity = await this.recipeRepository.create(recipeEntity); + throw new ValidationError("Trying to update recipe without ID!") + } else { + // save existing Recipe + // First: Load current version of recipe from database + const recipeEntity = await this.recipeRepository.findById(recipeId); + if(!recipeEntity){ + throw new ValidationError("No recipe with ID " + recipeId + " found in database!") + } + // merge changes into entity + this.mapper.mergeDtoIntoEntity(dto, recipeEntity); + // persist changes + savedEntity = await this.recipeRepository.update(recipeEntity); + } + return this.mapper.toDto(savedEntity); + } /** * Update recipe data * @param RecipeDto containing the entire updated recipe