Fix bug when saving tags for recipes
This commit is contained in:
parent
66da81baf8
commit
96d87fed13
8 changed files with 81 additions and 17 deletions
|
|
@ -5,7 +5,7 @@ meta {
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{url}}/recipe/fa608340-d679-4267-8b89-c743bd7fc234
|
url: {{url}}/recipe/44a8f38c-9387-439e-aed6-c3369b776b1c
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
bruno/recipe-backend/TagRestResource/createOrUpdate.bru
Normal file
21
bruno/recipe-backend/TagRestResource/createOrUpdate.bru
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
meta {
|
||||||
|
name: createOrUpdate
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{url}}/tag/create-or-update
|
||||||
|
body: json
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"description": "Kuchen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
}
|
||||||
15
bruno/recipe-backend/TagRestResource/delete.bru
Normal file
15
bruno/recipe-backend/TagRestResource/delete.bru
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
meta {
|
||||||
|
name: delete
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
url: {{url}}/tag/374d5df2-f7ff-45cd-ac67-c213233bf83f
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
}
|
||||||
8
bruno/recipe-backend/TagRestResource/folder.bru
Normal file
8
bruno/recipe-backend/TagRestResource/folder.bru
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
meta {
|
||||||
|
name: TagRestResource
|
||||||
|
seq: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
auth {
|
||||||
|
mode: inherit
|
||||||
|
}
|
||||||
15
bruno/recipe-backend/TagRestResource/getAll.bru
Normal file
15
bruno/recipe-backend/TagRestResource/getAll.bru
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
meta {
|
||||||
|
name: getAll
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{url}}/tag/all
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
}
|
||||||
|
|
@ -8,9 +8,9 @@ import {requireAdmin} from "../../middleware/authorizationMiddleware.js";
|
||||||
* REST resource for tags.
|
* REST resource for tags.
|
||||||
*
|
*
|
||||||
* Routes:
|
* Routes:
|
||||||
* GET /tags — list all tags (authenticated users)
|
* GET /tag — list all tags (authenticated users)
|
||||||
* POST /tags/create-or-update — create or update a tag (authenticated users)
|
* POST /tag/create-or-update — create or update a tag (authenticated users)
|
||||||
* DELETE /tags/:id — delete a tag (administrators only)
|
* DELETE /tag/:id — delete a tag (administrators only)
|
||||||
*
|
*
|
||||||
* Authentication / authorisation is assumed to be enforced by middleware
|
* Authentication / authorisation is assumed to be enforced by middleware
|
||||||
* applied upstream (e.g. a global JWT guard). The adminOnly middleware below
|
* applied upstream (e.g. a global JWT guard). The adminOnly middleware below
|
||||||
|
|
@ -22,7 +22,7 @@ const tagHandler = new TagHandler();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /tags
|
* GET /tag/all
|
||||||
* Returns all available tags.
|
* Returns all available tags.
|
||||||
*/
|
*/
|
||||||
router.get("/all", async (_req: Request, res: Response, next: NextFunction) => {
|
router.get("/all", async (_req: Request, res: Response, next: NextFunction) => {
|
||||||
|
|
@ -35,9 +35,9 @@ router.get("/all", async (_req: Request, res: Response, next: NextFunction) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /tags/create-or-update
|
* POST /tag/create-or-update
|
||||||
* Creates a new tag or updates an existing one.
|
* Creates a new tag or updates an existing one.
|
||||||
* If a tag with the given descriprion already exists in the database, that tag is returned instead of creating
|
* If a tag with the given description already exists in the database, that tag is returned instead of creating
|
||||||
* a duplicate.
|
* a duplicate.
|
||||||
* Body: TagDto
|
* Body: TagDto
|
||||||
*/
|
*/
|
||||||
|
|
@ -55,7 +55,7 @@ router.post(
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DELETE /tags/:id
|
* DELETE /tag/:id
|
||||||
* Deletes a tag by ID. Restricted to administrators.
|
* Deletes a tag by ID. Restricted to administrators.
|
||||||
* The database cascade removes all entries in the recipe_tag mapping table.
|
* The database cascade removes all entries in the recipe_tag mapping table.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { AbstractDtoEntityMapper } from "./AbstractDtoEntityMapper.js";
|
||||||
import { RecipeIngredientGroupDtoEntityMapper } from "./RecipeIngredientGroupDtoEntityMapper.js";
|
import { RecipeIngredientGroupDtoEntityMapper } from "./RecipeIngredientGroupDtoEntityMapper.js";
|
||||||
import { RecipeInstructionStepDtoEntityMapper } from "./RecipeInstructionStepDtoEntityMapper.js";
|
import { RecipeInstructionStepDtoEntityMapper } from "./RecipeInstructionStepDtoEntityMapper.js";
|
||||||
import { TagDtoEntityMapper } from "./TagDtoEntityMapper.js";
|
import { TagDtoEntityMapper } from "./TagDtoEntityMapper.js";
|
||||||
|
import {TagEntity} from "../entities/TagEntity.js";
|
||||||
|
|
||||||
export class RecipeDtoEntityMapper extends AbstractDtoEntityMapper<RecipeEntity, RecipeDto> {
|
export class RecipeDtoEntityMapper extends AbstractDtoEntityMapper<RecipeEntity, RecipeDto> {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
@ -114,12 +115,15 @@ export class RecipeDtoEntityMapper extends AbstractDtoEntityMapper<RecipeEntity,
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- Tags ---
|
// --- Tags ---
|
||||||
// Tags are looked up by ID and replaced wholesale: the recipe holds
|
/* Only build a stub here instead of using the mapper to avoid causing a cascade on the tag table while
|
||||||
// references to existing tag entities, so we map each DTO to its entity
|
* updating the join table correctly. For this purpose, the ORM only needs to know the tag ID in order to
|
||||||
// form and let TypeORM sync the join table on save.
|
* signal that we are dealing with an existing tag.
|
||||||
entity.tagList = (dto.tagList ?? []).map((tagDto) =>
|
*/
|
||||||
this.tagMapper.toEntity(tagDto)
|
entity.tagList = (dto.tagList ?? []).map((tagDto) => {
|
||||||
);
|
const stub = new TagEntity();
|
||||||
|
stub.id = tagDto.id;
|
||||||
|
return stub;
|
||||||
|
});
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { AbstractRepository } from "./AbstractRepository.js";
|
import { AbstractRepository } from "./AbstractRepository.js";
|
||||||
import { RecipeEntity } from "../entities/RecipeEntity.js";
|
import { RecipeEntity } from "../entities/RecipeEntity.js";
|
||||||
import { AppDataSource } from "../data-source.js";
|
import { AppDataSource } from "../data-source.js";
|
||||||
import { ILike, Like } from "typeorm";
|
import { ILike } from "typeorm";
|
||||||
|
|
||||||
export class RecipeRepository extends AbstractRepository<RecipeEntity> {
|
export class RecipeRepository extends AbstractRepository<RecipeEntity> {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -19,7 +19,8 @@ export class RecipeRepository extends AbstractRepository<RecipeEntity> {
|
||||||
relations: [
|
relations: [
|
||||||
'ingredientGroups',
|
'ingredientGroups',
|
||||||
'ingredientGroups.ingredients',
|
'ingredientGroups.ingredients',
|
||||||
'instructionSteps'
|
'instructionSteps',
|
||||||
|
'tagList',
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +47,7 @@ export class RecipeRepository extends AbstractRepository<RecipeEntity> {
|
||||||
// load existing data
|
// load existing data
|
||||||
const existing = await this.repo.findOneOrFail({
|
const existing = await this.repo.findOneOrFail({
|
||||||
where: { id: entity.id },
|
where: { id: entity.id },
|
||||||
relations: ["instructionSteps", "ingredientGroups", "ingredientGroups.ingredients"],
|
relations: ["instructionSteps", "ingredientGroups", "ingredientGroups.ingredients", "tagList"],
|
||||||
});
|
});
|
||||||
|
|
||||||
// merge new entity and existing entity
|
// merge new entity and existing entity
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue