fix relations
This commit is contained in:
parent
58ef7fbc00
commit
0587153829
8 changed files with 64 additions and 79 deletions
|
|
@ -5,51 +5,63 @@ meta {
|
|||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:4000/recipe
|
||||
url: http://localhost:4000/recipe/create-or-update
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImE0NDdlNDM0LTQyMWYtNDJiYS04MGRlLTM0ZDE1YzJmNWE2YyIsImlhdCI6MTc1ODk4Njk4MSwiZXhwIjoxNzU5MDczMzgxfQ.rYvECzhI3Tptse3yVjZvR9RXgs1gkwAt2_5-hpAXvB0
|
||||
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImE0NDdlNDM0LTQyMWYtNDJiYS04MGRlLTM0ZDE1YzJmNWE2YyIsImlhdCI6MTc2MDExNzA3MywiZXhwIjoxNzYwMjAzNDczfQ.NEfrUuzFcxocgN52uhptku5QVUbg03nmrN1E6A6XycA
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"title": "Spaghetti mit Tomatensosse",
|
||||
"amount": "4",
|
||||
"amountDescription": "Personen",
|
||||
"title": "Apfelkuchen Edeltrud 2",
|
||||
"amount": "1",
|
||||
"amountDescription": "Kuchen",
|
||||
"instructions": [{
|
||||
"text": "Spaghetti nach Packungsanleitung zubereiten",
|
||||
"text": "Mürbteig von 400 g Mehl herstellen",
|
||||
"sortOrder": 1
|
||||
},
|
||||
{
|
||||
"text": "Tomatensosse erhitzen",
|
||||
"text": "Äpfel schälen und kleinschneiden.",
|
||||
"sortOrder": 2
|
||||
},
|
||||
{
|
||||
"text": "Vermischen, mit geriebenem Parmesan bestreuen und servieren",
|
||||
"text": "Springform fetten, zwei Drittel des Teigs hineindrücken, Äpfel mit Rosinen vermischen und einfüllen, restlichen Teig ausrollen, damit abdecken und mit Milch bepinseln",
|
||||
"sortOrder": 3
|
||||
},
|
||||
{
|
||||
"text": "Backen",
|
||||
"sortOrder": 4
|
||||
}],
|
||||
"ingredientGroups": [
|
||||
{
|
||||
"title": "Für den Teig",
|
||||
"sortOrder": 1,
|
||||
"ingredients":[
|
||||
{
|
||||
"name": "Spaghetti",
|
||||
"amount": 500,
|
||||
"name": "Mehl",
|
||||
"amount": 400,
|
||||
"unit": "g",
|
||||
"sortOrder": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Für die Füllung",
|
||||
"sortOrder": 2,
|
||||
"ingredients":[
|
||||
{
|
||||
"name": "große Äpfel",
|
||||
"amount": 5,
|
||||
"sortOrder": 1
|
||||
},
|
||||
{
|
||||
"name": "Tomatensosse",
|
||||
"amount": 1,
|
||||
"unit": "Glas",
|
||||
"name": "Rosinen",
|
||||
"amount": 100,
|
||||
"unit": "g",
|
||||
"sortOrder": 2
|
||||
},
|
||||
{
|
||||
"name": "Parmesan",
|
||||
"sortOrder": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ meta {
|
|||
seq: 7
|
||||
}
|
||||
|
||||
put {
|
||||
url: http://localhost:4000/recipe/44a8f38c-9387-439e-aed6-c3369b776b1c
|
||||
post {
|
||||
url: http://localhost:4000/recipe/create-or-update
|
||||
body: json
|
||||
auth: bearer
|
||||
}
|
||||
|
||||
auth:bearer {
|
||||
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImE0NDdlNDM0LTQyMWYtNDJiYS04MGRlLTM0ZDE1YzJmNWE2YyIsImlhdCI6MTc1OTE3MjI3MywiZXhwIjoxNzU5MjU4NjczfQ._X_ZtBGtx0_14Nx90ctSQL-ieVPptaPc7WjG3FnyOOA
|
||||
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImE0NDdlNDM0LTQyMWYtNDJiYS04MGRlLTM0ZDE1YzJmNWE2YyIsImlhdCI6MTc1OTU5MTk1MiwiZXhwIjoxNzU5Njc4MzUyfQ.gkvuBtq8OaC7OqnArPcrV7jd34Ll7jHYXRbvz847aiw
|
||||
}
|
||||
|
||||
body:json {
|
||||
|
|
@ -23,13 +23,6 @@ body:json {
|
|||
"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",
|
||||
|
|
@ -43,13 +36,6 @@ body:json {
|
|||
"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",
|
||||
|
|
@ -57,9 +43,13 @@ body:json {
|
|||
"updatedAt": "2025-09-28T10:24:05.429Z",
|
||||
"text": "Backen",
|
||||
"sortOrder": 5
|
||||
}, {
|
||||
"text": "Essen",
|
||||
"sortOrder": 6
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
],
|
||||
"ingredientGroups": [
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ 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
|
||||
|
|
@ -22,20 +21,6 @@ const recipeInstructionStepMapper = new RecipeInstructionStepDtoEntityMapper();
|
|||
const recipeMapper = new RecipeDtoEntityMapper(recipeInstructionStepMapper, recipeIngredientGroupMapper);
|
||||
const recipeController = new RecipeHandler(recipeRepository, recipeMapper);
|
||||
|
||||
/**
|
||||
* Create new recipe
|
||||
* Consumes: RecipeDto
|
||||
* Responds with RecipeDto
|
||||
* DEPRECATED!
|
||||
*/
|
||||
router.post(
|
||||
"/",
|
||||
asyncHandler(async (req, res) => {
|
||||
const requestDto : RecipeDto = req.body;
|
||||
const responseDto = await recipeController.createRecipe(requestDto);
|
||||
res.status(201).json(responseDto);
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Save or update recipe.
|
||||
|
|
@ -67,24 +52,4 @@ router.get(
|
|||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Saves existing recipe
|
||||
* Also handles changes to instructions steps and ingredient (groups)
|
||||
* Consumes: RecipeDto
|
||||
* Responds with RecipeDto
|
||||
* DEPRECATED
|
||||
*/
|
||||
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;
|
||||
|
|
@ -16,4 +16,12 @@ export abstract class AbstractEntity {
|
|||
|
||||
@UpdateDateColumn({name: "update_date"})
|
||||
updateDate?: Date;
|
||||
|
||||
/**
|
||||
* Checks whether entity has a valid ID
|
||||
* @todo check for valid UUID...
|
||||
*/
|
||||
hasValidId() : boolean {
|
||||
return this.id !== undefined && this.id.length > 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@ export class RecipeHandler {
|
|||
}
|
||||
var savedEntity: RecipeEntity;
|
||||
const recipeId = dto.id
|
||||
if(recipeId === undefined){
|
||||
if(recipeId === undefined || recipeId.length === 0){
|
||||
// create new recipe
|
||||
const recipeEntity = this.mapper.toEntity(dto)
|
||||
delete (recipeEntity as any).id;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -43,8 +43,10 @@ export class RecipeDtoEntityMapper extends AbstractDtoEntityMapper<RecipeEntity,
|
|||
entity.instructionSteps = dto.instructions.map((stepDto) => {
|
||||
const stepEntity = this.instructionStepMapper.toEntity(stepDto);
|
||||
|
||||
// Always set the relation
|
||||
stepEntity.recipe = entity;
|
||||
// Set the relation if the entity already exists in DB
|
||||
if(entity.hasValidId()){
|
||||
stepEntity.recipe = entity;
|
||||
}
|
||||
|
||||
// If it's a new step (no id from client), let DB generate a new UUID
|
||||
if (!stepDto.id) {
|
||||
|
|
@ -57,7 +59,11 @@ export class RecipeDtoEntityMapper extends AbstractDtoEntityMapper<RecipeEntity,
|
|||
// map ingredient groups
|
||||
entity.ingredientGroups = dto.ingredientGroups.map((groupDto) => {
|
||||
const groupEntity = this.ingredientGroupMapper.toEntity(groupDto);
|
||||
groupEntity.recipe = entity;
|
||||
// Set the relation if the entity already exists in DB
|
||||
if(entity.hasValidId()){
|
||||
groupEntity.recipe = entity;
|
||||
}
|
||||
// If it's a new group (no id from client), let DB generate a new UUID
|
||||
if (!groupDto.id) {
|
||||
delete (groupEntity as any).id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,10 @@ export class RecipeIngredientGroupDtoEntityMapper extends AbstractDtoEntityMappe
|
|||
// map ingredients
|
||||
entity.ingredients = dto.ingredients.map((ingredientDto) => {
|
||||
const ingredientEntity = this.ingredientMapper.toEntity(ingredientDto);
|
||||
ingredientEntity.ingredientGroup = entity;
|
||||
// set relation if entity already exists in DB
|
||||
if(entity.hasValidId()){
|
||||
ingredientEntity.ingredientGroup = entity;
|
||||
}
|
||||
// remove id from new entity completely and allow ORM to generate a new one
|
||||
if (!ingredientDto.id) {
|
||||
delete (ingredientEntity as any).id;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ import { Request, Response, NextFunction } from "express";
|
|||
*/
|
||||
export function corsHeaders (req: Request, res: Response, next: NextFunction) {
|
||||
// allow requests from all sources (*) for now
|
||||
// @todo restrict access!
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
|
||||
res.header('Access-Control-Allow-Methods', 'GET,POST,DELETE,OPTIONS');
|
||||
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
|
||||
// Handle preflight requests quickly
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue