add CompactRecipePoint for loading recipe header data

This commit is contained in:
Anika Raemer 2025-09-28 18:41:56 +02:00
parent 380eb4cd21
commit 3638909761
8 changed files with 97 additions and 35 deletions

View file

@ -1,11 +1,11 @@
meta { meta {
name: getAllRecipes name: getCompactRecipes
type: http type: http
seq: 5 seq: 5
} }
get { get {
url: http://localhost:4000/recipe url: http://localhost:4000/compact-recipe
body: none body: none
auth: bearer auth: bearer
} }

View file

@ -0,0 +1,28 @@
import { CompactRecipeDto } from "../dtos/CompactRecipeDto.js";
import { RecipeEntity } from "../entities/RecipeEntity.js";
import { CompactRecipeDtoEntityMapper } from "../mappers/CompactRecipeDtoEntityMapper.js";
import { RecipeRepository } from "../repositories/RecipeRepository.js";
/**
* Responsible for loading recipe header data
*/
export class CompactRecipeController {
constructor(
private repository: RecipeRepository,
private mapper: CompactRecipeDtoEntityMapper
) {}
/**
* Load list of all recipes
* @returns List of all recipes
*/
async getAllCompactRecipes() {
const recipeEntities: RecipeEntity[] = await this.repository.findAll();
// @todo load instruction steps, ingredient groups and ingredients before mapping!
let recipeDtos: CompactRecipeDto[] = [];
recipeEntities.forEach(recipeEntity => {
recipeDtos.push(this.mapper.toDto(recipeEntity));
});
return recipeDtos;
}
}

View file

@ -1,14 +1,13 @@
import { RecipeDto } from "../dtos/RecipeDto.js"; import { RecipeDto } from "../dtos/RecipeDto.js";
import { RecipeDtoEntityMapper } from "../mappers/RecipeDtoEntityMapper.js"; import { RecipeDtoEntityMapper } from "../mappers/RecipeDtoEntityMapper.js";
import { RecipeRepository } from "../repositories/RecipeRepository.js"; import { RecipeRepository } from "../repositories/RecipeRepository.js";
import { RecipeEntity } from "../entities/RecipeEntity.js";
import { ValidationError } from "../errors/httpErrors.js"; import { ValidationError } from "../errors/httpErrors.js";
import { RecipeInstructionStepDto } from "../dtos/RecipeInstructionStepDto.js"; import { RecipeInstructionStepDto } from "../dtos/RecipeInstructionStepDto.js";
import { RecipeIngredientGroupDto } from "../dtos/RecipeIngredientGroupDto.js"; import { RecipeIngredientGroupDto } from "../dtos/RecipeIngredientGroupDto.js";
import { RecipeIngredientDto } from "../dtos/RecipeIngredientDto.js"; import { RecipeIngredientDto } from "../dtos/RecipeIngredientDto.js";
/** /**
* Controls all user specific actions * Controls all recipe specific actions
*/ */
export class RecipeController { export class RecipeController {
constructor( constructor(
@ -16,19 +15,6 @@ export class RecipeController {
private mapper: RecipeDtoEntityMapper private mapper: RecipeDtoEntityMapper
) { } ) { }
/**
* Load list of all recipes
* @returns List of all recipes
*/
async getAllRecipes() {
const recipeEntities : RecipeEntity[] = await this.repository.findAll();
let recipeDtos: RecipeDto[] = [];
recipeEntities.forEach(recipeEntity => {
recipeDtos.push(this.mapper.toDto(recipeEntity));
});
return recipeDtos;
}
/** /**
* Create a new recipe * Create a new recipe
* @param dto RecipeDto containing the new recipe * @param dto RecipeDto containing the new recipe
@ -48,7 +34,7 @@ export class RecipeController {
* @param dto RecipeDTO * @param dto RecipeDTO
* @returns true if the recipe is valid * @returns true if the recipe is valid
*/ */
protected isRecipeDtoValid(dto: RecipeDto): boolean { private isRecipeDtoValid(dto: RecipeDto): boolean {
return dto.title !== undefined return dto.title !== undefined
&& dto.title.length !== 0 && dto.title.length !== 0
@ -64,7 +50,7 @@ export class RecipeController {
* @param ingredientGroups Array of ingredient groups * @param ingredientGroups Array of ingredient groups
* @returns true if all ingredient groups are valid * @returns true if all ingredient groups are valid
*/ */
protected isIngredientGroupsValid(ingredientGroups: RecipeIngredientGroupDto[] | undefined): boolean { private isIngredientGroupsValid(ingredientGroups: RecipeIngredientGroupDto[] | undefined): boolean {
return ingredientGroups !== undefined return ingredientGroups !== undefined
&& ingredientGroups.length !== 0 && ingredientGroups.length !== 0
&& ingredientGroups.every(group => { && ingredientGroups.every(group => {
@ -80,7 +66,7 @@ export class RecipeController {
* @param ingredients Array if ingredients * @param ingredients Array if ingredients
* @returns true if the ingredient list is valid * @returns true if the ingredient list is valid
*/ */
isIngredientsValid(ingredients: RecipeIngredientDto[] | undefined): boolean { private isIngredientsValid(ingredients: RecipeIngredientDto[] | undefined): boolean {
return ingredients !== undefined return ingredients !== undefined
&& ingredients.length !== 0 && ingredients.length !== 0
&& ingredients.every(ingredient => { && ingredients.every(ingredient => {
@ -92,7 +78,7 @@ export class RecipeController {
* @param ingredient RecipeIngredientDto * @param ingredient RecipeIngredientDto
* @returns true if the ingredient is valid * @returns true if the ingredient is valid
*/ */
isIngredientValid(ingredient: RecipeIngredientDto): boolean { private isIngredientValid(ingredient: RecipeIngredientDto): boolean {
return ingredient !== null return ingredient !== null
&& ingredient.sortOrder !== undefined && ingredient.sortOrder !== undefined
&& ingredient.name !== undefined && ingredient.name !== undefined
@ -106,7 +92,7 @@ export class RecipeController {
* @param instructions Array if instruction step DTOs * @param instructions Array if instruction step DTOs
* @returns Boolean indicating whether the instruction steps are valid * @returns Boolean indicating whether the instruction steps are valid
*/ */
protected isInstructionsValid(instructions: RecipeInstructionStepDto[] | undefined): boolean { private isInstructionsValid(instructions: RecipeInstructionStepDto[] | undefined): boolean {
return instructions !== undefined return instructions !== undefined
&& instructions.length !== 0 && instructions.length !== 0
&& instructions.every(step => { && instructions.every(step => {

View file

@ -0,0 +1,11 @@
import { AbstractDto } from "./AbstractDto.js";
/**
* DTO describing the essential header data of a recipe
* Used to populate lists
*/
export class CompactRecipeDto extends AbstractDto {
title!: string;
// @todo add resource and rating here once implemented!
}

View file

@ -0,0 +1,27 @@
import { Router } from "express";
import { asyncHandler } from "../utils/asyncHandler.js";
import { RecipeRepository } from "../repositories/RecipeRepository.js";
import { CompactRecipeController } from "../controllers/CompactRecipeController.js";
import { CompactRecipeDtoEntityMapper } from "../mappers/CompactRecipeDtoEntityMapper.js";
/**
* Handles all recipe related routes
*/
const router = Router();
// Inject repo + mapper here
const recipeRepository = new RecipeRepository();
const compactRecipeMapper = new CompactRecipeDtoEntityMapper();
const compactRecipeController = new CompactRecipeController(recipeRepository, compactRecipeMapper);
/**
* Load header data of all recipes
*/
router.get(
"/",
asyncHandler(async (req, res) => {
const response = await compactRecipeController.getAllCompactRecipes();
res.status(201).json(response);
})
);
export default router;

View file

@ -9,7 +9,7 @@ import { RecipeIngredientGroupDtoEntityMapper } from "../mappers/RecipeIngredien
import { RecipeInstructionStepDtoEntityMapper } from "../mappers/RecipeInstructionStepDtoEntityMapper.js"; import { RecipeInstructionStepDtoEntityMapper } from "../mappers/RecipeInstructionStepDtoEntityMapper.js";
/** /**
* Handles all user related routes * Handles all recipe related routes
*/ */
const router = Router(); const router = Router();
@ -21,7 +21,6 @@ const recipeInstructionStepMapper = new RecipeInstructionStepDtoEntityMapper();
const recipeMapper = new RecipeDtoEntityMapper(recipeInstructionStepMapper, recipeIngredientGroupMapper); const recipeMapper = new RecipeDtoEntityMapper(recipeInstructionStepMapper, recipeIngredientGroupMapper);
const recipeController = new RecipeController(recipeRepository, recipeMapper); const recipeController = new RecipeController(recipeRepository, recipeMapper);
/** /**
* Create new recipe * Create new recipe
*/ */
@ -34,15 +33,4 @@ router.post(
}) })
); );
/**
* Load all recipes
*/
router.get(
"/",
asyncHandler(async (req, res) => {
const response = await recipeController.getAllRecipes();
res.status(201).json(response);
})
);
export default router; export default router;

View file

@ -4,6 +4,7 @@ import dotenv from "dotenv";
import { AppDataSource } from "./data-source.js"; import { AppDataSource } from "./data-source.js";
import authRoutes, { authBasicRoute } from "./endpoints/AuthPoint.js"; import authRoutes, { authBasicRoute } from "./endpoints/AuthPoint.js";
import userRoutes from "./endpoints/UserPoint.js"; import userRoutes from "./endpoints/UserPoint.js";
import compactRecipeRoutes from "./endpoints/CompactRecipePoint.js";
import recipeRoutes from "./endpoints/RecipePoint.js"; import recipeRoutes from "./endpoints/RecipePoint.js";
import { errorHandler } from "./middleware/errorHandler.js"; import { errorHandler } from "./middleware/errorHandler.js";
import { authentication } from "./middleware/authenticationMiddleware.js"; import { authentication } from "./middleware/authenticationMiddleware.js";
@ -33,6 +34,7 @@ async function startServer() {
app.use(authBasicRoute, authRoutes); app.use(authBasicRoute, authRoutes);
app.use("/user", userRoutes); app.use("/user", userRoutes);
app.use("/recipe", recipeRoutes); app.use("/recipe", recipeRoutes);
app.use("/compact-recipe", compactRecipeRoutes);
// Error handling for all rest-calls // Error handling for all rest-calls
// must come last! // must come last!

View file

@ -0,0 +1,20 @@
import { CompactRecipeDto } from "../dtos/CompactRecipeDto.js";
import { RecipeEntity } from "../entities/RecipeEntity.js";
import { AbstractDtoEntityMapper } from "./AbstractDtoEntityMapper.js";
export class CompactRecipeDtoEntityMapper extends AbstractDtoEntityMapper<RecipeEntity,CompactRecipeDto>{
toDto(entity: RecipeEntity): CompactRecipeDto {
const dto = new CompactRecipeDto();
this.mapBaseEntityToDto(entity, dto);
dto.title = entity.title;
return dto;
}
toEntity(dto: CompactRecipeDto): RecipeEntity {
throw new Error("Mapping CompactRecipeDto to RecipeEntity is not allowed!");
}
}