diff --git a/server/db.sqlite3 b/server/db.sqlite3 index 5d06b7ba..0c8ecbb1 100644 Binary files a/server/db.sqlite3 and b/server/db.sqlite3 differ diff --git a/server/src/application/post/create/create-post.usecase.ts b/server/src/application/post/create/create-post.usecase.ts index f8e265a1..9820780e 100644 --- a/server/src/application/post/create/create-post.usecase.ts +++ b/server/src/application/post/create/create-post.usecase.ts @@ -1,10 +1,11 @@ import { BaseUseCase } from '@application/shared'; import { CreatePostRequest } from './create-post.request'; -import { PostDetailsResponseDto } from '@contracts/dtos/posts'; import { inject, injectable } from 'inversify'; import { TYPES } from '@infrastructure/shared/ioc/types'; import { IPostRepository } from '@domain/repositories/post.repository'; import { LOGGER } from '@/web/rest/logger'; +import { PostDetailsResponseDto } from '@contracts/dtos/posts/post-details.response'; +import { log } from 'console'; @injectable() class CreatePostUseCase extends BaseUseCase< @@ -20,8 +21,14 @@ class CreatePostUseCase extends BaseUseCase< public async performOperation( request: CreatePostRequest, ): Promise { + LOGGER.info('CreatePostUseCase.performOperation'); const post = CreatePostRequest.toEntity(request); + LOGGER.info( + 'mapped post is kjkhkhkhfkldhskjhdfksjahjdshajkhfdjskahfsdhjksah', + post.toString(), + ); const createdPost = await this._postRepository.createPost(post); + log('created post is', createdPost); if (createdPost) { return PostDetailsResponseDto.fromEntity(createdPost); } diff --git a/server/src/application/post/find-all/find-all-post.usecase.ts b/server/src/application/post/find-all/find-all-post.usecase.ts index af3a8c28..46035d22 100644 --- a/server/src/application/post/find-all/find-all-post.usecase.ts +++ b/server/src/application/post/find-all/find-all-post.usecase.ts @@ -4,6 +4,7 @@ import { PostResponseDto } from '@contracts/dtos/posts'; import { TYPES } from '@infrastructure/shared/ioc/types'; import { IPostRepository } from '@domain/repositories/post.repository'; import { inject } from 'inversify'; +import { log } from 'console'; class FindAllPostUseCase extends BaseUseCase< FindAllPostRequest, @@ -21,6 +22,7 @@ class FindAllPostUseCase extends BaseUseCase< ): Promise { const { pageSize, pageNumber } = request; const posts = await this._postRepository.findAll(pageSize, pageNumber); + log('posts from use-case', posts); const postDtos = await Promise.all( posts.map(async post => { return await PostResponseDto.fromEntity(post); diff --git a/server/src/contracts/dtos/posts/post-details.response.ts b/server/src/contracts/dtos/posts/post-details.response.ts index 08175ba4..a3a1f9bd 100644 --- a/server/src/contracts/dtos/posts/post-details.response.ts +++ b/server/src/contracts/dtos/posts/post-details.response.ts @@ -1,6 +1,8 @@ +import { LOGGER } from '@/web/rest/logger'; import { Post } from '@domain/entities'; import { Nullable } from '@domain/shared/types'; import { UserResponseDto } from '@dtos/users'; +import { log } from 'console'; export class PostDetailsResponseDto { constructor( diff --git a/server/src/contracts/dtos/posts/post.response.ts b/server/src/contracts/dtos/posts/post.response.ts index 2fb258d0..92a36c6c 100644 --- a/server/src/contracts/dtos/posts/post.response.ts +++ b/server/src/contracts/dtos/posts/post.response.ts @@ -1,7 +1,7 @@ import { Nullable } from '@domain/shared/types'; import { CommentResponseDto } from '@dtos/comments'; import { UserResponseDto } from '@dtos/users'; -import { Comment, Post } from '@domain/entities'; +import { Post } from '@domain/entities'; export class PostResponseDto { constructor( @@ -12,7 +12,7 @@ export class PostResponseDto { public comments: CommentResponseDto[], public createdAt: Date, public lastModifiedAt: Nullable, - ) { } + ) {} public static async fromEntity(entity: Post): Promise { return new PostResponseDto( @@ -20,7 +20,7 @@ export class PostResponseDto { entity.title, entity.content, UserResponseDto.fromEntity(entity.author), - entity.comments ? (await entity.comments).map(CommentResponseDto.fromEntity) : [], + entity.comments ? entity.comments.map(CommentResponseDto.fromEntity) : [], entity.createdAt, entity.updatedAt, ); diff --git a/server/src/contracts/dtos/users/user.response.ts b/server/src/contracts/dtos/users/user.response.ts index 902ae687..cf8a7212 100644 --- a/server/src/contracts/dtos/users/user.response.ts +++ b/server/src/contracts/dtos/users/user.response.ts @@ -8,7 +8,7 @@ export class UserResponseDto { public username: string, public email: string, public pictureProfile?: string, - ) { } + ) {} public static fromEntity(entity: User): UserResponseDto { return new UserResponseDto( diff --git a/server/src/infrastructure/posts/post.mapper.ts b/server/src/infrastructure/posts/post.mapper.ts index 67df0832..4e355c11 100644 --- a/server/src/infrastructure/posts/post.mapper.ts +++ b/server/src/infrastructure/posts/post.mapper.ts @@ -4,20 +4,29 @@ import { Post } from '@domain/entities'; import { UserMapper } from '@infrastructure/users'; import { CommentMapper } from '@infrastructure/comments'; import { LikePostMapper } from '@infrastructure/like-posts'; -import { log } from 'console'; +import { MapperConfig } from '@infrastructure/shared/persistence/mapper.config'; class PostMapper { - public static async toDomain(persistence: PostPersistence): Promise { - log(`Post Persistence after loading : `, await persistence.likes); - const likes = await persistence.likes; - const comments = await persistence.comments; + public static async toDomain( + persistence: PostPersistence, + config: MapperConfig = {}, + ): Promise { + const likes = config.includeLikes + ? await (persistence.likes ?? Promise.resolve([])) + : []; + const comments = config.includeComments + ? await (persistence.comments ?? Promise.resolve([])) + : []; + const author = config.includeAuthor + ? await UserMapper.toDomain(await persistence.author) + : null; return Post.create( persistence.id, persistence.title, persistence.content, persistence.authorId, - await UserMapper.toDomain(persistence.author), + author as any, await Promise.all(likes.map(like => LikePostMapper.toDomain(like))), await Promise.all( comments.map(comment => CommentMapper.toDomain(comment)), @@ -29,7 +38,10 @@ class PostMapper { ); } - public static async toPersistence(domain: Post): Promise { + public static async toPersistence( + domain: Post, + config: MapperConfig = {}, + ): Promise { const postPersistence = new PostPersistence(); if (domain.id) { postPersistence.id = domain.id; @@ -38,18 +50,26 @@ class PostMapper { postPersistence.title = domain.title; postPersistence.content = domain.content; postPersistence.authorId = domain.authorId; - postPersistence.author = await UserMapper.toPersistence(domain.author); - postPersistence.likes = Promise.resolve( - await Promise.all( - domain.likes.map(like => LikePostMapper.toPersistence(like)), - ), - ); - postPersistence.comments = Promise.resolve( - await Promise.all( - domain.comments.map(comment => CommentMapper.toPersistence(comment)), - ), - ); + if (config.includeAuthor) { + postPersistence.author = UserMapper.toPersistence(domain.author); + } + + if (config.includeLikes) { + postPersistence.likes = Promise.resolve( + await Promise.all( + domain.likes.map(like => LikePostMapper.toPersistence(like)), + ), + ); + } + + if (config.includeComments) { + postPersistence.comments = Promise.resolve( + await Promise.all( + domain.comments.map(comment => CommentMapper.toPersistence(comment)), + ), + ); + } postPersistence.status = domain.status.toLowerCase(); postPersistence.createdAt = domain.createdAt; diff --git a/server/src/infrastructure/posts/post.persistence.ts b/server/src/infrastructure/posts/post.persistence.ts index b1557887..8de0215d 100644 --- a/server/src/infrastructure/posts/post.persistence.ts +++ b/server/src/infrastructure/posts/post.persistence.ts @@ -7,6 +7,7 @@ import { UpdateDateColumn, CreateDateColumn, PrimaryGeneratedColumn, + JoinTable, } from 'typeorm'; import { CommentPersistence } from '@infrastructure/comments/'; import { UserPersistence } from '@infrastructure/users'; @@ -36,15 +37,18 @@ class PostPersistence { @Column() authorId: string; - @ManyToOne(() => UserPersistence, user => user.posts, { eager: true }) - author: UserPersistence; + @ManyToOne(() => UserPersistence, user => user.posts) + @JoinTable() + author: Promise; @OneToMany(() => CommentPersistence, comment => comment.post, { lazy: true }) + @JoinTable() comments: Promise; @OneToMany(() => LikePostPersistence, likePost => likePost.post, { lazy: true, }) + @JoinTable() likes: Promise; // add status column enum ['draft', 'published', 'deleted'] diff --git a/server/src/infrastructure/posts/post.repository.impl.ts b/server/src/infrastructure/posts/post.repository.impl.ts index d01989c2..204c6f5c 100644 --- a/server/src/infrastructure/posts/post.repository.impl.ts +++ b/server/src/infrastructure/posts/post.repository.impl.ts @@ -5,7 +5,6 @@ import { Post } from '@domain/entities'; import { IPostRepository } from '@domain/repositories/post.repository'; import { PostPersistence } from './post.persistence'; import { PostMapper } from './post.mapper'; -import { LOGGER } from '@/web/rest/logger'; import { log } from 'console'; @injectable() @@ -23,7 +22,7 @@ export class PostRepository implements IPostRepository { async createPost(post: Post): Promise { const postPersistence = await PostMapper.toPersistence(post); const createdPost = await this._repository.save(postPersistence); - return PostMapper.toDomain(createdPost); + return PostMapper.toDomain(createdPost, { includeAuthor: true }); } async findPostById(postId: string): Promise { @@ -48,7 +47,9 @@ export class PostRepository implements IPostRepository { async findAll(limit: number, page: number): Promise { const [posts, count] = await this._repository.findAndCount(); - const postPromises = posts.map(post => PostMapper.toDomain(post)); + const postPromises = posts.map(post => + PostMapper.toDomain(post, { includeAuthor: true }), + ); return Promise.all(postPromises); } diff --git a/server/src/infrastructure/shared/persistence/mapper.config.ts b/server/src/infrastructure/shared/persistence/mapper.config.ts new file mode 100644 index 00000000..438e38d3 --- /dev/null +++ b/server/src/infrastructure/shared/persistence/mapper.config.ts @@ -0,0 +1,12 @@ +interface MapperConfig { + includeComments?: boolean; + includePosts?: boolean; + includeLikedPosts?: boolean; + includeLikedReplies?: boolean; + includeReplies?: boolean; + includeLikedComments?: boolean; + includeLikes?: boolean; + includeAuthor?: boolean; +} + +export { MapperConfig }; diff --git a/server/src/infrastructure/shared/persistence/mapper.facade.ts b/server/src/infrastructure/shared/persistence/mapper.facade.ts new file mode 100644 index 00000000..98d73359 --- /dev/null +++ b/server/src/infrastructure/shared/persistence/mapper.facade.ts @@ -0,0 +1,97 @@ +import { LikeReply, Reply, User } from '@domain/entities/'; +import { Post } from '@domain/entities/'; +import { Comment } from '@domain/entities'; +import { LikePost } from '@domain/entities/'; +import { UserPersistence, UserMapper } from '@infrastructure/users/'; +import { PostPersistence, PostMapper } from '@infrastructure/posts/'; +import { CommentPersistence, CommentMapper } from '@infrastructure/comments/'; +import { + LikePostPersistence, + LikePostMapper, +} from '@infrastructure/like-posts/'; +import { ReplyMapper, ReplyPersistence } from '@infrastructure/replies'; +import { + LikeReplyMapper, + LikeReplyPersistence, +} from '@infrastructure/like-replies'; +import { + LikeCommentMapper, + LikeCommentPersistence, +} from '@infrastructure/like-comments'; +import { LikeComment } from '@domain/entities/like-comment'; + +class MapperFacade { + static async toUserDomain(persistence: UserPersistence): Promise { + return await UserMapper.toDomain(persistence); + } + + static async toUserPersistence(domain: User): Promise { + return await UserMapper.toPersistence(domain); + } + + static async toPostDomain(persistence: PostPersistence): Promise { + return await PostMapper.toDomain(persistence); + } + + static async toPostPersistence(domain: Post): Promise { + return await PostMapper.toPersistence(domain); + } + + static async toCommentDomain( + persistence: CommentPersistence, + ): Promise { + return await CommentMapper.toDomain(persistence); + } + + static async toCommentPersistence( + domain: Comment, + ): Promise { + return await CommentMapper.toPersistence(domain); + } + + static async toLikePostDomain( + persistence: LikePostPersistence, + ): Promise { + return await LikePostMapper.toDomain(persistence); + } + + static async toLikePostPersistence( + domain: LikePost, + ): Promise { + return await LikePostMapper.toPersistence(domain); + } + + static async toLikeCommentDomain( + persistence: LikeCommentPersistence, + ): Promise { + return await LikeCommentMapper.toDomain(persistence); + } + + static async toLikeCommentPersistence( + domain: LikeComment, + ): Promise { + return await LikeCommentMapper.toPersistence(domain); + } + + static async toLikeReplyDomain( + persistence: LikeReplyPersistence, + ): Promise { + return await LikeReplyMapper.toDomain(persistence); + } + + static async toLikeReplyPersistence( + domain: LikeReply, + ): Promise { + return await LikeReplyMapper.toPersistence(domain); + } + + static async toReplyDomain(persistence: ReplyPersistence): Promise { + return await ReplyMapper.toDomain(persistence); + } + + static async toReplyPersistence(domain: Reply): Promise { + return await ReplyMapper.toPersistence(domain); + } +} + +export { MapperFacade }; diff --git a/server/src/infrastructure/users/user.mapper.ts b/server/src/infrastructure/users/user.mapper.ts index 3a356648..8d7b25c0 100644 --- a/server/src/infrastructure/users/user.mapper.ts +++ b/server/src/infrastructure/users/user.mapper.ts @@ -6,15 +6,31 @@ import { LikeReplyMapper } from '@infrastructure/like-replies/like-reply.mapper' import { CommentMapper } from '@infrastructure/comments/comment.mapper'; import { PostMapper } from '@infrastructure/posts/post.mapper'; import { ReplyMapper } from '@infrastructure/replies/reply.mapper'; +import { MapperConfig } from '@infrastructure/shared/persistence/mapper.config'; class UserMapper { - static async toDomain(persistence: UserPersistence): Promise { - const comments = await (persistence.comments ?? Promise.resolve([])); - const posts = await (persistence.posts ?? Promise.resolve([])); - const likedPosts = await (persistence.likedPosts ?? Promise.resolve([])); - const likedReplies = await (persistence.likedReplies ?? Promise.resolve([])); - const replies = await (persistence.replies ?? Promise.resolve([])); - const likedComments = await (persistence.likedComments ?? Promise.resolve([])); + static async toDomain( + persistence: UserPersistence, + config: MapperConfig = {}, + ): Promise { + const comments = config.includeComments + ? await (persistence.comments ?? Promise.resolve([])) + : []; + const posts = config.includePosts + ? await (persistence.posts ?? Promise.resolve([])) + : []; + const likedPosts = config.includeLikedPosts + ? await (persistence.likedPosts ?? Promise.resolve([])) + : []; + const likedReplies = config.includeLikedReplies + ? await (persistence.likedReplies ?? Promise.resolve([])) + : []; + const replies = config.includeReplies + ? await (persistence.replies ?? Promise.resolve([])) + : []; + const likedComments = config.includeLikedComments + ? await (persistence.likedComments ?? Promise.resolve([])) + : []; return User.create( persistence.id, @@ -28,19 +44,34 @@ class UserMapper { persistence.id ?? '', persistence.updatedAt, persistence.id ?? '', - await Promise.all(comments.map(comment => CommentMapper.toDomain(comment))), - await Promise.all(likedComments.map(likeComment => LikeCommentMapper.toDomain(likeComment))), - await Promise.all(posts.map(post => PostMapper.toDomain(post)),), - await Promise.all(likedPosts.map(likePost => LikePostMapper.toDomain(likePost))), + await Promise.all( + comments.map(comment => CommentMapper.toDomain(comment)), + ), + await Promise.all( + likedComments.map(likeComment => + LikeCommentMapper.toDomain(likeComment), + ), + ), + await Promise.all(posts.map(post => PostMapper.toDomain(post))), + await Promise.all( + likedPosts.map(likePost => LikePostMapper.toDomain(likePost)), + ), await Promise.all(replies.map(reply => ReplyMapper.toDomain(reply))), - await Promise.all(likedReplies.map(likeReply => LikeReplyMapper.toDomain(likeReply))), + await Promise.all( + likedReplies.map(likeReply => LikeReplyMapper.toDomain(likeReply)), + ), ); } - static async toPersistence(domain: User): Promise { + static async toPersistence( + domain: User, + config: MapperConfig = {}, + ): Promise { const userPersistence = new UserPersistence(); - userPersistence.id = domain.id; + if (domain.id) { + userPersistence.id = domain.id; + } userPersistence.username = domain.username; userPersistence.email = domain.email; userPersistence.firstName = domain.firstName; @@ -51,41 +82,61 @@ class UserMapper { userPersistence.updatedAt = domain.updatedAt; userPersistence.deletedAt = domain.deletedAt; - userPersistence.likedPosts = Promise.resolve( - await Promise.all( - domain.likedPosts?.map(likePost => LikePostMapper.toPersistence(likePost)) ?? [] - ) - ); + if (config.includeLikedPosts) { + userPersistence.likedPosts = Promise.resolve( + await Promise.all( + domain.likedPosts?.map(likePost => + LikePostMapper.toPersistence(likePost), + ) ?? [], + ), + ); + } - userPersistence.replies = Promise.resolve( - await Promise.all( - domain.replies?.map(reply => ReplyMapper.toPersistence(reply)) ?? [] - ) - ); + if (config.includeReplies) { + userPersistence.replies = Promise.resolve( + await Promise.all( + domain.replies?.map(reply => ReplyMapper.toPersistence(reply)) ?? [], + ), + ); + } - userPersistence.comments = Promise.resolve( - await Promise.all( - domain.comments?.map(comment => CommentMapper.toPersistence(comment)) ?? [] - ) - ); + if (config.includeComments) { + userPersistence.comments = Promise.resolve( + await Promise.all( + domain.comments?.map(comment => + CommentMapper.toPersistence(comment), + ) ?? [], + ), + ); + } - userPersistence.posts = Promise.resolve( - await Promise.all( - domain.posts?.map(post => PostMapper.toPersistence(post)) ?? [] - ) - ); + if (config.includePosts) { + userPersistence.posts = Promise.resolve( + await Promise.all( + domain.posts?.map(post => PostMapper.toPersistence(post)) ?? [], + ), + ); + } - userPersistence.likedComments = Promise.resolve( - await Promise.all( - domain.likedComments?.map(likeComment => LikeCommentMapper.toPersistence(likeComment)) ?? [] - ) - ); + if (config.includeLikedComments) { + userPersistence.likedComments = Promise.resolve( + await Promise.all( + domain.likedComments?.map(likeComment => + LikeCommentMapper.toPersistence(likeComment), + ) ?? [], + ), + ); + } - userPersistence.likedReplies = Promise.resolve( - await Promise.all( - domain.likedReplies?.map(likeReply => LikeReplyMapper.toPersistence(likeReply)) ?? [] - ) - ); + if (config.includeLikedReplies) { + userPersistence.likedReplies = Promise.resolve( + await Promise.all( + domain.likedReplies?.map(likeReply => + LikeReplyMapper.toPersistence(likeReply), + ) ?? [], + ), + ); + } return userPersistence; } diff --git a/server/src/web/process.log b/server/src/web/process.log index 00c1cd7a..8ead4c8d 100644 --- a/server/src/web/process.log +++ b/server/src/web/process.log @@ -1,3 +1,3 @@ -{"level":30,"time":1717071773671,"msg":"Application is running on port 2105 🚀"} -{"level":30,"time":1717071773674,"msg":"Environment: development"} -{"level":30,"time":1717071773694,"msg":"Data source initialized"} +{"level":30,"time":1717153656237,"msg":"Application is running on port 2105 🚀"} +{"level":30,"time":1717153656240,"msg":"Environment: development"} +{"level":30,"time":1717153656261,"msg":"Data source initialized"} diff --git a/server/src/web/rest/controllers/posts.controller.ts b/server/src/web/rest/controllers/posts.controller.ts index 3246296e..ac78b2dc 100644 --- a/server/src/web/rest/controllers/posts.controller.ts +++ b/server/src/web/rest/controllers/posts.controller.ts @@ -4,12 +4,13 @@ import { CreatePostResponseDto } from '@contracts/dtos/posts/create/create-post. import { TriggeredByUser } from '@domain/shared/entities'; import { TYPES } from '@infrastructure/shared/ioc/types'; import { inject, injectable } from 'inversify'; -import { ExpressHandler } from '../infrastucture/express-handler'; import BaseController from './base.controller'; import { LOGGER } from '../logger'; import { POST_STATUS } from '@domain/eums/post-status.enum'; import { FindAllPostRequest } from '@application/post/find-all/find-all-post.request'; import { PostResponseDto } from '@contracts/dtos/posts'; +import { ExpressHandler } from '../infrastucture/express-handler'; +import { log } from 'console'; @injectable() export class PostsController implements BaseController { @@ -21,27 +22,24 @@ export class PostsController implements BaseController { FindAllPostRequest, PostResponseDto[] >; + constructor( @inject(TYPES.ICreatePostInputPort) - _createPostInteractor: BaseUseCase< - CreatePostRequest, - CreatePostResponseDto - >, + createPostInteractor: BaseUseCase, @inject(TYPES.IFindAllPostInputPort) - _findAllPostInteractor: BaseUseCase, + findAllPostInteractor: BaseUseCase, ) { - this._createPostUseCase = _createPostInteractor; - this._findAllPostUseCase = _findAllPostInteractor; + this._createPostUseCase = createPostInteractor; + this._findAllPostUseCase = findAllPostInteractor; } public create: ExpressHandler = - async (req, res) => { + async (req: any, res: any) => { LOGGER.info('PostsController.create'); const { title, content } = req.body; if (!title || !content) { return res.status(400).json({}); } - const request = CreatePostRequest.create( new TriggeredByUser(res.locals.userId, []), title!, @@ -51,19 +49,23 @@ export class PostsController implements BaseController { [], POST_STATUS.DRAFT, ); + log('request', request.toString()); const result = await this._createPostUseCase.execute(request); + log('result after creation', result); return res.json(result); }; - public findAll: ExpressHandler = - async (req, res) => { + async (req: any, res: any) => { LOGGER.info('PostsController.findAll'); + const pageSize = parseInt(req.query.pageSize as string) || 10; + const pageNumber = parseInt(req.query.pageNumber as string) || 1; + const request = FindAllPostRequest.create( new TriggeredByUser(res.locals.userId, []), - req.query.pageSize || 10, - req.query.pageNumber || 1, + pageSize, + pageNumber, ); const result = await this._findAllPostUseCase.execute(request); - return result; + return res.json(result); }; }