diff --git a/src/data-source.ts b/src/data-source.ts index 89636d4..d0b32e5 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -11,9 +11,16 @@ const __dirname = dirname(__filename); dotenv.config(); +/** + * Load config + */ const { DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE, NODE_ENV } = process.env; + /** + * Configures data source + */ + export const AppDataSource = new DataSource({ type: "postgres", host: DB_HOST, diff --git a/src/endpoints/AuthPoint.ts b/src/endpoints/AuthPoint.ts index 7745240..f2b91fb 100644 --- a/src/endpoints/AuthPoint.ts +++ b/src/endpoints/AuthPoint.ts @@ -1,5 +1,5 @@ import { Router } from "express"; -import { AuthController } from "../controllers/AuthController.js"; +import { AuthHandler } from "../handlers/AuthHandler.js"; import { UserRepository } from "../repositories/UserRepository.js"; import { UserDtoEntityMapper } from "../mappers/UserDtoEntityMapper.js"; import { @@ -14,8 +14,13 @@ export const authBasicRoute = "/auth" const router = Router(); const userRepository = new UserRepository(); const mapper = new UserDtoEntityMapper(); -const authController = new AuthController(userRepository, mapper); +const authController = new AuthHandler(userRepository, mapper); +/** + * Login using username and password + * Consumes LoginRequestDto + * Responds with LoginResponseDto + */ router.post("/login", async (req, res) => { console.log("login point called") try { diff --git a/src/endpoints/CompactRecipePoint.ts b/src/endpoints/CompactRecipePoint.ts index ea1db47..17e6d49 100644 --- a/src/endpoints/CompactRecipePoint.ts +++ b/src/endpoints/CompactRecipePoint.ts @@ -1,7 +1,7 @@ import { Router } from "express"; import { asyncHandler } from "../utils/asyncHandler.js"; import { RecipeRepository } from "../repositories/RecipeRepository.js"; -import { CompactRecipeController } from "../controllers/CompactRecipeController.js"; +import { CompactRecipeHandler } from "../handlers/CompactRecipeHandler.js"; import { CompactRecipeDtoEntityMapper } from "../mappers/CompactRecipeDtoEntityMapper.js"; /** @@ -12,9 +12,10 @@ const router = Router(); // Inject repo + mapper here const recipeRepository = new RecipeRepository(); const compactRecipeMapper = new CompactRecipeDtoEntityMapper(); -const compactRecipeController = new CompactRecipeController(recipeRepository, compactRecipeMapper); +const compactRecipeController = new CompactRecipeHandler(recipeRepository, compactRecipeMapper); /** * Load header data of all recipes + * Responds with a list of CompactRecipeDtos */ router.get( "/", diff --git a/src/endpoints/RecipePoint.ts b/src/endpoints/RecipePoint.ts index dde7a17..3949add 100644 --- a/src/endpoints/RecipePoint.ts +++ b/src/endpoints/RecipePoint.ts @@ -1,7 +1,7 @@ import { Router } from "express"; import { RecipeRepository } from "../repositories/RecipeRepository.js"; import { RecipeDtoEntityMapper } from "../mappers/RecipeDtoEntityMapper.js"; -import { RecipeController } from "../controllers/RecipeController.js"; +import { RecipeHandler } from "../handlers/RecipeHandler.js"; import { asyncHandler } from "../utils/asyncHandler.js"; import { RecipeDto } from "../dtos/RecipeDto.js"; import { RecipeIngredientDtoEntityMapper } from "../mappers/RecipeIngredientDtoEntityMapper.js"; @@ -20,10 +20,12 @@ const recipeIngredientMapper = new RecipeIngredientDtoEntityMapper(); const recipeIngredientGroupMapper = new RecipeIngredientGroupDtoEntityMapper(recipeIngredientMapper); const recipeInstructionStepMapper = new RecipeInstructionStepDtoEntityMapper(); const recipeMapper = new RecipeDtoEntityMapper(recipeInstructionStepMapper, recipeIngredientGroupMapper); -const recipeController = new RecipeController(recipeRepository, recipeMapper); +const recipeController = new RecipeHandler(recipeRepository, recipeMapper); /** * Create new recipe + * Consumes: RecipeDto + * Responds with RecipeDto */ router.post( "/", @@ -34,6 +36,10 @@ router.post( }) ); +/** + * Load recipe by id + * Responds with RecipeDto + */ router.get( "/:id", asyncHandler(async(req, res) => { @@ -43,6 +49,12 @@ router.get( }) ); +/** + * Saves existing recipe + * Also handles changes to instructions steps and ingredient (groups) + * Consumes: RecipeDto + * Responds with RecipeDto + */ router.put( "/:id", asyncHandler(async(req, res) =>{ diff --git a/src/endpoints/UserPoint.ts b/src/endpoints/UserPoint.ts index edf9167..58d8266 100644 --- a/src/endpoints/UserPoint.ts +++ b/src/endpoints/UserPoint.ts @@ -1,5 +1,5 @@ import { Router } from "express"; -import { UserController } from "../controllers/UserController.js"; +import { UserHandler } from "../handlers/UserHandler.js"; import { CreateUserRequestDto } from "../dtos/CreateUserRequestDto.js"; import { UserRepository } from "../repositories/UserRepository.js"; import { UserDtoEntityMapper } from "../mappers/UserDtoEntityMapper.js"; @@ -13,10 +13,12 @@ const router = Router(); // Inject repo + mapper here const userRepository = new UserRepository(); const userMapper = new UserDtoEntityMapper(); -const userController = new UserController(userRepository, userMapper); +const userController = new UserHandler(userRepository, userMapper); /** * Create a new user + * Consumes CreateUserRequestDto + * Responds with UserDto */ router.post( "/", @@ -29,6 +31,7 @@ router.post( /** * Get user data for current user + * Responds with UserDto */ router.get("/me", asyncHandler(async (req, res) => { diff --git a/src/entities/AbstractEntity.ts b/src/entities/AbstractEntity.ts index 86401fd..07fb2c2 100644 --- a/src/entities/AbstractEntity.ts +++ b/src/entities/AbstractEntity.ts @@ -4,6 +4,9 @@ import { UpdateDateColumn, } from "typeorm"; +/** + * Abstract entity containing basic fields that all entities have in common + */ export abstract class AbstractEntity { @PrimaryGeneratedColumn("uuid") id?: string; diff --git a/src/entities/UserEntity.ts b/src/entities/UserEntity.ts index c306eac..f53b82c 100644 --- a/src/entities/UserEntity.ts +++ b/src/entities/UserEntity.ts @@ -1,7 +1,9 @@ import { Entity, Column } from "typeorm"; import { AbstractEntity } from "./AbstractEntity.js"; -// @todo Add migration to update table +/** + * Entity describing a user + */ @Entity({ name: "user" }) export class UserEntity extends AbstractEntity { @Column({ nullable: false, name: "user_name" }) diff --git a/src/controllers/AuthController.ts b/src/handlers/AuthHandler.ts similarity index 98% rename from src/controllers/AuthController.ts rename to src/handlers/AuthHandler.ts index 15664a6..32065fb 100644 --- a/src/controllers/AuthController.ts +++ b/src/handlers/AuthHandler.ts @@ -9,7 +9,7 @@ import { LoginRequestDto } from "../dtos/LoginRequestDto.js"; * Controller responsible for authentication, e.g., login or issueing a token with extended * lifetime */ -export class AuthController { +export class AuthHandler { constructor( private userRepository: UserRepository, private mapper: UserDtoEntityMapper diff --git a/src/controllers/CompactRecipeController.ts b/src/handlers/CompactRecipeHandler.ts similarity index 96% rename from src/controllers/CompactRecipeController.ts rename to src/handlers/CompactRecipeHandler.ts index 395f3d9..dfcebf9 100644 --- a/src/controllers/CompactRecipeController.ts +++ b/src/handlers/CompactRecipeHandler.ts @@ -6,7 +6,7 @@ import { RecipeRepository } from "../repositories/RecipeRepository.js"; /** * Responsible for loading recipe header data */ -export class CompactRecipeController { +export class CompactRecipeHandler { constructor( private repository: RecipeRepository, private mapper: CompactRecipeDtoEntityMapper diff --git a/src/controllers/RecipeController.ts b/src/handlers/RecipeHandler.ts similarity index 97% rename from src/controllers/RecipeController.ts rename to src/handlers/RecipeHandler.ts index da6b8c1..eee5eda 100644 --- a/src/controllers/RecipeController.ts +++ b/src/handlers/RecipeHandler.ts @@ -5,13 +5,11 @@ 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"; -import { RecipeEntity } from "../entities/RecipeEntity.js"; /** * Controls all recipe specific actions */ -export class RecipeController { +export class RecipeHandler { constructor( private recipeRepository: RecipeRepository, private mapper: RecipeDtoEntityMapper diff --git a/src/controllers/UserController.ts b/src/handlers/UserHandler.ts similarity index 98% rename from src/controllers/UserController.ts rename to src/handlers/UserHandler.ts index 68ad0ef..bccb8e3 100644 --- a/src/controllers/UserController.ts +++ b/src/handlers/UserHandler.ts @@ -9,7 +9,7 @@ import { UUID } from "crypto"; /** * Controls all user specific actions */ -export class UserController { +export class UserHandler { constructor( private userRepository: UserRepository, private mapper: UserDtoEntityMapper diff --git a/src/mappers/AbstractDtoEntityMapper.ts b/src/mappers/AbstractDtoEntityMapper.ts index d2ea746..dd878bc 100644 --- a/src/mappers/AbstractDtoEntityMapper.ts +++ b/src/mappers/AbstractDtoEntityMapper.ts @@ -25,12 +25,16 @@ export abstract class AbstractDtoEntityMapper< return entity; } - // Abstract methods to be implemented by subclasses - abstract toDto(entity: E): D; - abstract toEntity(dto: D): E; - abstract mergeDtoIntoEntity(dto: D, entity: E): E; - abstract createNewEntity() : E; - + /** + * Merge entity list with changes contained in DTO list + * @param dtos List of dtos + * @param entities List of entities + * @returns Merged list + * + * elements no longer contained in the dto list will be removed from the entity list + * new elements will be mapped to entity and added to the entity list + * existing elements will be updated + */ mergeDtoListIntoEntityList(dtos: D[], entities: E[]) : E[]{ const updatedEntities: E[] = []; const existingMap = new Map(entities?.map(e => [e.id, e]) ?? []); @@ -49,4 +53,29 @@ export abstract class AbstractDtoEntityMapper< return updatedEntities; } + + // Abstract methods to be implemented by subclasses + /** + * Maps an entity to DTO + * @param entity Entity that is mapped to DTO + */ + abstract toDto(entity: E): D; + /** + * Maps a DTO to entity + * @param dto DTO to map to entity + */ + abstract toEntity(dto: D): E; + /** + * Merge changes in DTO into entity + * @param dto Dto containing changes + * @param entity existing entity + * + * Used for merging user changes (DTO) into the existing entity (database). + */ + abstract mergeDtoIntoEntity(dto: D, entity: E): E; + /** + * Defines how to create a new entity. Required by mergeDtoListIntoEntityList + * to add new elements to the list + */ + abstract createNewEntity() : E; } diff --git a/src/mappers/CompactRecipeDtoEntityMapper.ts b/src/mappers/CompactRecipeDtoEntityMapper.ts index 8f2c2c4..b8040a3 100644 --- a/src/mappers/CompactRecipeDtoEntityMapper.ts +++ b/src/mappers/CompactRecipeDtoEntityMapper.ts @@ -17,11 +17,11 @@ export class CompactRecipeDtoEntityMapper extends AbstractDtoEntityMapper { protected repo: Repository; @@ -22,15 +25,6 @@ export abstract class AbstractRepository { return this.repo.save(entity); } - /* async update(id: string, partialData: DeepPartial): Promise { - await this.repo.update(id as any, partialData); - const updated = await this.findById(id); - if (!updated) { - throw new Error("Entity not found after update"); - } - return updated; - } */ - async delete(id: string): Promise { await this.repo.delete(id as any); }