From 4b0e2237e5194bccba3aaf204eea5aa6ac59c441 Mon Sep 17 00:00:00 2001 From: PleBea Date: Sun, 14 Jul 2024 04:09:47 +0900 Subject: [PATCH] refactor: Remove unused code and dependencies in invite-response.dto.ts and plan.controller.ts --- prisma/schema.prisma | 19 +-- src/modules/plan/dto/invite-response.dto.ts | 33 +++- src/modules/plan/dto/plan.dto.ts | 20 +++ src/modules/plan/plan.controller.ts | 41 ++--- src/modules/plan/plan.service.ts | 158 ++++++++++---------- 5 files changed, 148 insertions(+), 123 deletions(-) create mode 100644 src/modules/plan/dto/plan.dto.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0ce7a6e..41421bf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -22,10 +22,10 @@ model User { UserProfileImage UserProfileImage[] planId String? Plan Plan? @relation(fields: [planId], references: [id]) - Invite Invite[] FCMToken String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + Invite Invite? } model UserProfileImage { @@ -61,18 +61,11 @@ model Place { Plan Plan @relation(fields: [planId], references: [id]) } -enum InviteStatus { - PENDING - ACCEPTED - REJECTED -} - model Invite { - id String @id @default(uuid()) - userId String - User User @relation(fields: [userId], references: [id]) + id String @id @default(uuid()) planId String - Plan Plan @relation(fields: [planId], references: [id]) - status InviteStatus - createdAt DateTime @default(now()) + Plan Plan @relation(fields: [planId], references: [id]) + userId String @unique + User User @relation(fields: [userId], references: [id]) + createdAt DateTime @default(now()) } diff --git a/src/modules/plan/dto/invite-response.dto.ts b/src/modules/plan/dto/invite-response.dto.ts index 479eda9..62a3e3c 100644 --- a/src/modules/plan/dto/invite-response.dto.ts +++ b/src/modules/plan/dto/invite-response.dto.ts @@ -1,9 +1,36 @@ -import { $Enums, Invite } from '@prisma/client'; +import { Invite, Plan, User } from '@prisma/client'; + +import { ApiProperty } from '@nestjs/swagger'; + +import { UserResponseDTO } from 'src/modules/user/dto/user.dto'; + +import { PlanDTO } from './plan.dto'; + +export class InviteLinkResponseDTO { + @ApiProperty() + link: string; +} export class InviteResponseDTO implements Invite { + @ApiProperty() id: string; - userId: string; + + @ApiProperty() planId: string; - status: $Enums.InviteStatus; + + @ApiProperty({ + type: UserResponseDTO, + }) + User: User; + + @ApiProperty() + userId: string; + + @ApiProperty({ + type: PlanDTO, + }) + Plan: Plan; + + @ApiProperty() createdAt: Date; } diff --git a/src/modules/plan/dto/plan.dto.ts b/src/modules/plan/dto/plan.dto.ts new file mode 100644 index 0000000..724c089 --- /dev/null +++ b/src/modules/plan/dto/plan.dto.ts @@ -0,0 +1,20 @@ +import { $Enums, Plan } from '@prisma/client'; + +import { ApiProperty } from '@nestjs/swagger'; + +export class PlanDTO implements Plan { + @ApiProperty() + id: string; + + @ApiProperty() + name: string; + + @ApiProperty() + date: string; + + @ApiProperty() + status: $Enums.PlanStatus; + + @ApiProperty() + createdAt: Date; +} diff --git a/src/modules/plan/plan.controller.ts b/src/modules/plan/plan.controller.ts index c86bd3e..425f1fd 100644 --- a/src/modules/plan/plan.controller.ts +++ b/src/modules/plan/plan.controller.ts @@ -9,6 +9,7 @@ import { ApiBearerAuth, ApiBody, ApiOkResponse, ApiOperation, ApiTags, ApiUnauth import { CurrentUser } from 'src/common'; import { CreatePlanDTO } from './dto/create-plan.dto'; +import { InviteLinkResponseDTO, InviteResponseDTO } from './dto/invite-response.dto'; import { PlanResponseDTO } from './dto/plan-response.dto'; import { PlanService } from './plan.service'; @@ -53,42 +54,40 @@ export class PlanController { return await this.planService.deletePlan(user); } - @Get('invite') + @Get('invite/:inviteId') @ApiBearerAuth() @UseGuards(AuthGuard('access')) @ApiOperation({ summary: 'Get plan invite' }) - @ApiOkResponse({ description: 'Plan invite', type: PlanResponseDTO }) + @ApiOkResponse({ description: 'Plan invite', type: InviteResponseDTO }) @ApiUnauthorizedResponse({ description: 'Unauthorized' }) - async getInvite(@CurrentUser() user: User) { - return await this.planService.getInvite(user); + async getInvite(@CurrentUser() user: User, @Param('inviteId') inviteId: string): Promise { + return await this.planService.getInvite(inviteId); } - @Get('invite/:inviteId') + @Post('invite') @ApiBearerAuth() @UseGuards(AuthGuard('access')) + @ApiOperation({ summary: 'Invite user to a plan' }) + @ApiOkResponse({ description: 'Plan invite sent', type: InviteLinkResponseDTO }) + @ApiUnauthorizedResponse({ description: 'Unauthorized' }) + async invite(@CurrentUser() user: User): Promise { + return { link: await this.planService.invite(user) }; + } + + @Get('invite/link/:inviteId') @ApiOperation({ summary: 'Deep link to plan invite' }) @ApiOkResponse({ description: 'Plan invite', type: PlanResponseDTO }) @ApiUnauthorizedResponse({ description: 'Unauthorized' }) async getInviteById(@Req() req: Request, @Param('inviteId') inviteId: string, @Res() res: Response) { if (req.headers['user-agent']?.toLocaleLowerCase().includes('kakaotalk')) { return res.redirect( - `kakaotalk://web/openExternal?url=${this.configService.get('BACKEND_URL')!}/plan/invite/${inviteId}`, + `kakaotalk://web/openExternal?url=${this.configService.get('BACKEND_URL')!}/plan/invite/link/${inviteId}`, ); } res.redirect(`adego-by-seogaemo://invite/${inviteId}`); } - @Post('invite/:userId') - @ApiBearerAuth() - @UseGuards(AuthGuard('access')) - @ApiOperation({ summary: 'Invite a user to the plan' }) - @ApiOkResponse({ description: 'User invited', type: PlanResponseDTO }) - @ApiUnauthorizedResponse({ description: 'Unauthorized' }) - async inviteUser(@CurrentUser() user: User, @Param('userId') userId: string) { - return await this.planService.inviteUser(user, userId); - } - @Put('invite/:inviteId') @ApiBearerAuth() @UseGuards(AuthGuard('access')) @@ -98,14 +97,4 @@ export class PlanController { async acceptInvite(@CurrentUser() user: User, @Param('inviteId') inviteId: string) { return await this.planService.acceptInvite(user, inviteId); } - - @Delete('invite/:inviteId') - @ApiBearerAuth() - @UseGuards(AuthGuard('access')) - @ApiOperation({ summary: 'Decline a plan invite' }) - @ApiOkResponse({ description: 'Plan invite reject', type: PlanResponseDTO }) - @ApiUnauthorizedResponse({ description: 'Unauthorized' }) - async rejectInvite(@CurrentUser() user: User, @Param('inviteId') inviteId: string) { - return await this.planService.rejectInvite(user, inviteId); - } } diff --git a/src/modules/plan/plan.service.ts b/src/modules/plan/plan.service.ts index 3474889..f77517b 100644 --- a/src/modules/plan/plan.service.ts +++ b/src/modules/plan/plan.service.ts @@ -1,7 +1,8 @@ -import { InviteStatus, Plan, PlanStatus, User } from '@prisma/client'; +import { Plan, PlanStatus, User } from '@prisma/client'; import { DateTime } from 'luxon'; import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { Cron, CronExpression } from '@nestjs/schedule'; import { FirebaseService } from 'src/common/modules/firebase/firebase.service'; @@ -17,6 +18,7 @@ export class PlanService { private readonly prisma: PrismaService, private readonly firebase: FirebaseService, private readonly addressService: AddressService, + private readonly configService: ConfigService, ) {} async getPlan(user: User): Promise { @@ -39,7 +41,61 @@ export class PlanService { return { ...plan, isAlarmAvailable: await this.isAlarmAvailable(plan.date) }; } - async createPlan({ id }: User, dto: CreatePlanDTO): Promise { + async getInvite(inviteId: string) { + const invite = await this.prisma.invite.findUnique({ + where: { + id: inviteId, + }, + include: { + Plan: true, + User: true, + }, + }); + + if (!invite) throw new HttpException('Invite not found', HttpStatus.NOT_FOUND); + if (!invite.Plan) throw new HttpException('Plan not found', HttpStatus.NOT_FOUND); + + return invite; + } + + getInviteLink(inviteId: string) { + return `${this.configService.get('BACKEND_URL')}/plan/invite/link/${inviteId}`; + } + + async invite({ id }: User) { + const user = await this.prisma.user.findUnique({ + where: { + id, + }, + include: { + Plan: true, + Invite: true, + }, + }); + + if (!user) throw new HttpException('User not found', HttpStatus.NOT_FOUND); + if (!user.Plan) throw new HttpException('User does not have a plan', HttpStatus.BAD_GATEWAY); + if (user.Invite) return this.getInviteLink(user.Invite.id); + + const invite = await this.prisma.invite.create({ + data: { + Plan: { + connect: { + id: user.Plan.id, + }, + }, + User: { + connect: { + id, + }, + }, + }, + }); + + return this.getInviteLink(invite.id); + } + + async createPlan({ id }: User, dto: CreatePlanDTO) { if (DateTime.fromISO(dto.date) < DateTime.now()) throw new HttpException('Invalid date', HttpStatus.BAD_REQUEST); @@ -55,12 +111,6 @@ export class PlanService { if (!user) return; if (user.Plan) throw new HttpException('User already has a plan', HttpStatus.BAD_GATEWAY); - await this.prisma.invite.deleteMany({ - where: { - userId: id, - }, - }); - const place = await this.addressService.getAddressByKeyword(dto.address); return { @@ -145,106 +195,52 @@ export class PlanService { return { ...res, isAlarmAvailable: await this.isAlarmAvailable(res.date) }; } - async getInvite({ id }: User) { - const invites = await this.prisma.invite.findMany({ - where: { - userId: id, - }, - include: { - Plan: true, - }, - }); - - return invites; - } - - async inviteUser({ id }: User, userId: string) { + async acceptInvite({ id }: User, inviteId: string) { const user = await this.prisma.user.findUnique({ where: { id, }, - include: { - Plan: true, - }, }); if (!user) throw new HttpException('User not found', HttpStatus.NOT_FOUND); - if (!user.Plan) throw new HttpException('User does not have a plan', HttpStatus.BAD_GATEWAY); - - const targetUser = await this.prisma.user.findUnique({ - where: { - id: userId, - }, - include: { - Plan: true, - }, - }); - - if (!targetUser) throw new HttpException('Target user not found', HttpStatus.NOT_FOUND); - if (targetUser.planId) throw new HttpException('Target user already has a plan', HttpStatus.BAD_GATEWAY); - return await this.prisma.invite.create({ - data: { - userId: userId, - planId: user.planId, - status: InviteStatus.PENDING, - }, - }); - } - - async acceptInvite({ id }: User, inviteId: string) { const invite = await this.prisma.invite.findUnique({ where: { id: inviteId, }, - include: { - Plan: true, - User: true, - }, }); if (!invite) throw new HttpException('Invite not found', HttpStatus.NOT_FOUND); - if (invite.userId !== id) throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED); - await this.prisma.user.update({ + const plan = await this.prisma.plan.findUnique({ where: { - id, - }, - data: { - planId: invite.planId, + id: invite.planId, }, - }); - - await this.prisma.invite.deleteMany({ - where: { - userId: id, + include: { + users: true, }, }); - return invite.Plan; - } + if (!plan) throw new HttpException('Plan not found', HttpStatus.NOT_FOUND); + if (plan.users.some((u) => u.id === id)) + throw new HttpException('User is already in the plan', HttpStatus.BAD_GATEWAY); - async rejectInvite({ id }: User, inviteId: string) { - const invite = await this.prisma.invite.findUnique({ + return await this.prisma.plan.update({ where: { - id: inviteId, + id: plan.id, }, - include: { - Plan: true, - User: true, + data: { + users: { + connect: { + id, + }, + }, }, - }); - - if (!invite) throw new HttpException('Invite not found', HttpStatus.NOT_FOUND); - if (invite.userId !== id) throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED); - - await this.prisma.invite.delete({ - where: { - id: inviteId, + include: { + users: true, + place: true, }, }); - - return invite.Plan; } async isAlarmAvailable(date: string) {