Skip to content

Commit

Permalink
Merge pull request #278 from skgndi12/feature/issue-164/implement-cre…
Browse files Browse the repository at this point in the history
…ate-reply-service-method

[#164] Implement create reply service method
  • Loading branch information
skgndi12 authored Mar 14, 2024
2 parents 0dbf90f + 6363665 commit 41886cb
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 2 deletions.
50 changes: 49 additions & 1 deletion api/src/core/services/review/review.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { AccessLevel } from '@prisma/client';

import { Reply, Review } from '@src/core/entities/review.entity';
import { User } from '@src/core/entities/user.entity';
import { ReplyRepository } from '@src/core/ports/reply.repository';
import {
CreateReplyParams,
ReplyRepository
} from '@src/core/ports/reply.repository';
import {
CreateReviewParams,
FindReviewsParams,
Expand All @@ -15,6 +18,8 @@ import {
} from '@src/core/ports/transaction.manager';
import { UserRepository } from '@src/core/ports/user.repository';
import {
CreateReplyDto,
CreateReplyResponse,
CreateReviewDto,
CreateReviewResponse,
DeleteReviewDto,
Expand Down Expand Up @@ -241,6 +246,49 @@ export class ReviewService {
);
};

public createReply = async (
dto: CreateReplyDto
): Promise<CreateReplyResponse> => {
const [user, reply] = await this.txManager.runInTransaction(
async (txClient: TransactionClient): Promise<[User, Reply]> => {
const userReplying = await this.userRepository.findById(
dto.requesterIdToken.userId,
txClient
);

if (
!dto.requesterIdToken.isAccessLevelAuthorized(
new AccessLevelEnum(AccessLevel.USER)
)
) {
throw new CustomError({
code: AppErrorCode.PERMISSIION_DENIED,
message: 'insufficient access level to create reply',
context: { dto, user: userReplying }
});
}

const params: CreateReplyParams = {
reviewId: dto.reviewId,
userId: dto.requesterIdToken.userId,
content: dto.content
};
const replyCreated = await this.replyRepository.create(
params,
txClient
);

return [userReplying, replyCreated];
},
IsolationLevel.READ_COMMITTED
);

return {
user,
reply
};
};

private extractUserIds = (entries: Review[] | Reply[]): string[] => {
const userIds: string[] = [];
entries.forEach((entry) => {
Expand Down
11 changes: 11 additions & 0 deletions api/src/core/services/review/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface UpdateReviewResponse {
review: Review;
}

export interface CreateReplyResponse {
user: User;
reply: Reply;
}

export interface CreateReviewDto {
requesterIdToken: AppIdToken;
title: string;
Expand Down Expand Up @@ -70,3 +75,9 @@ export interface DeleteReviewDto {
requesterIdToken: AppIdToken;
reviewId: number;
}

export interface CreateReplyDto {
requesterIdToken: AppIdToken;
reviewId: number;
content: string;
}
95 changes: 94 additions & 1 deletion api/test/core/services/review/review.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { DeepMockProxy, mockClear, mockDeep } from 'jest-mock-extended';
import extendedPrisma from '@root/test/infrastructure/prisma/test.prisma.client';

import { AppIdToken } from '@src/core/entities/auth.entity';
import { Review } from '@src/core/entities/review.entity';
import { Reply, Review } from '@src/core/entities/review.entity';
import { User } from '@src/core/entities/user.entity';
import { ReplyRepository } from '@src/core/ports/reply.repository';
import { ReviewRepository } from '@src/core/ports/review.repository';
import { TransactionManager } from '@src/core/ports/transaction.manager';
import { UserRepository } from '@src/core/ports/user.repository';
import { ReviewService } from '@src/core/services/review/review.service';
import {
CreateReplyDto,
CreateReviewDto,
DeleteReviewDto,
GetReviewsDto,
Expand Down Expand Up @@ -722,4 +723,96 @@ describe('Test review service', () => {
expect(reviewRepository.deleteById).toBeCalledTimes(0);
});
});

describe('Test create reply', () => {
const userId = 'randomId';
const nickname = 'randomNickname';
const tag = '#TAGG';
const idp = new IdpEnum(Idp.GOOGLE);
const email = '[email protected]';
const accessLevel = new AccessLevelEnum(AccessLevel.USER);
const reviewId = 0;
const replyId = 1;
const content = 'randomContent';
const currentDate = new Date();
const requesterIdToken = new AppIdToken(
userId,
nickname,
tag,
idp,
email,
accessLevel
);

const userFound = new User(
userId,
nickname,
tag,
idp,
email,
accessLevel,
currentDate,
currentDate
);
const replyCreated = new Reply(
replyId,
reviewId,
requesterIdToken.userId,
content,
currentDate,
currentDate
);

const userFindById = jest.fn(() => Promise.resolve(userFound)) as jest.Mock;
const replyCreate = jest.fn(() =>
Promise.resolve(replyCreated)
) as jest.Mock;

beforeAll(() => {
prismaMock.$transaction.mockImplementation((callback) =>
callback(prismaMock)
);
userRepository = new PostgresqlUserRepository(prismaMock);
reviewRepository = new PostgresqlReviewRepository(prismaMock);
replyRepository = new PostgresqlReplyRepository(prismaMock);
txManager = new PrismaTransactionManager(prismaMock);
userRepository.findById = userFindById;
replyRepository.create = replyCreate;
});

it('should success when valid', async () => {
const givenDto: CreateReplyDto = {
requesterIdToken,
reviewId,
content
};
const actualResult = await new ReviewService(
userRepository,
reviewRepository,
replyRepository,
txManager
).createReply(givenDto);

expect(JSON.stringify(actualResult.user)).toEqual(
JSON.stringify(userFound)
);
expect(JSON.stringify(actualResult.reply)).toEqual(
JSON.stringify(replyCreated)
);

expect(userRepository.findById).toBeCalledTimes(1);
const userFindByIdArgs = userFindById.mock.calls[0][0];
expect(userFindByIdArgs).toEqual(givenDto.requesterIdToken.userId);

expect(replyRepository.create).toBeCalledTimes(1);
const replyCreateArgs = replyCreate.mock.calls[0][0];
expect(replyCreateArgs).toEqual(
expect.objectContaining({
reviewId: givenDto.reviewId,
userId: givenDto.requesterIdToken.userId,
content: givenDto.content
})
);
});
});
});

0 comments on commit 41886cb

Please sign in to comment.