diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 058135d..607480d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,37 +1,23 @@ ## 업데이트 유형 + - [ ] Hot Fix - [ ] Release - [ ] Develop - [ ] Others ## 업데이트 개요 + * Fix # (issue) * Feature # (issue) -[여기에 PR 한줄 요약 작성, 대상 이슈번호 작성.] - + [여기에 PR 한줄 요약 작성, 대상 이슈번호 작성.] ## 업데이트 요약 -업데이트 내역 작성 - -## 새로운 정책 -세부 변경 사항 작성 -## 해결한 문제 -해결한 문제 등 이슈 - -## 미해결 문제 -미해결 이슈, 발견한 이슈 - -## 테스트 -- [ ] 유닛 테스트 -- [ ] 빌드 테스트 (통합 테스트) -- [ ] 기타 유효성 테스트 +업데이트 내역 작성 ## 수정 사항 진단 + - [ ] 코드가 이 프로젝트의 스타일 지침을 따릅니다. - [ ] 코드에 대한 자체 리뷰를 수행했습니다. -- [ ] 변경 내역에 대해 주석을 작성했습니다. -- [ ] 해당 PR에 대한 문서를 변경했습니다. - [ ] 기능, 정책, 수정 사항, 이슈에 대한 테스트 코드를 추가했습니다. - [ ] 변경 내용이 로컬 개발환경에서의 테스트를 통과했습니다. -- [ ] 모든 종속 변경 사항이 병합되어 다운스트림 모듈에 게시되었습니다. diff --git a/src/entities/contest.entity.ts b/src/entities/contest.entity.ts index 5b40444..4022ef6 100644 --- a/src/entities/contest.entity.ts +++ b/src/entities/contest.entity.ts @@ -53,8 +53,6 @@ export class Contest { this.endTime = endTime; this.badge = badge; this.background = background; - - return this; } } diff --git a/src/entities/group.entity.ts b/src/entities/group.entity.ts index 25900bf..cbe3b58 100644 --- a/src/entities/group.entity.ts +++ b/src/entities/group.entity.ts @@ -1,22 +1,31 @@ -import { Prop, SchemaFactory } from '@nestjs/mongoose'; -import { IsMongoId, IsString } from 'class-validator'; -import { ObjectId } from 'mongodb'; +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; +export type GroupDocument = HydratedDocument; + +@Schema() export class Group { - @Prop({ type: ObjectId }) - @IsMongoId() id: string; @Prop({ required: true }) - @IsString() name: string; @Prop() - @IsString() description: string; @Prop({ type: [String], ref: 'Poker' }) pokers: string[]; + + constructor(id: string, name: string, description: string, pokers: string[]) { + this.id = id; + this.name = name; + this.description = description; + this.pokers = pokers; + } + + static fromDocument(groupDocument: GroupDocument): Group { + return new Group(groupDocument._id.toString(), groupDocument.name, groupDocument.description, groupDocument.pokers); + } } export const GroupSchema = SchemaFactory.createForClass(Group); diff --git a/src/entities/participant.entity.ts b/src/entities/participant.entity.ts index bd26e71..a9997c5 100644 --- a/src/entities/participant.entity.ts +++ b/src/entities/participant.entity.ts @@ -1,14 +1,56 @@ -import { SchemaFactory } from '@nestjs/mongoose'; +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; import { Problem } from './problem.entity'; +export type ParticipantDocument = HydratedDocument; + +@Schema() export class Participant { + @Prop({ required: true }) handle: string; + + @Prop({ required: true }) profileImage: string; + + @Prop({ required: true }) snapshot: number[]; + + @Prop({ required: true }) point: number; + + @Prop({ required: true }) goal: number; + + @Prop({ required: true }) result: Problem[]; + + constructor( + handle: string, + profileImage: string, + snapshot: number[], + point: number, + goal: number, + result: Problem[], + ) { + this.handle = handle; + this.profileImage = profileImage; + this.snapshot = snapshot; + this.point = point; + this.goal = goal; + this.result = result; + } + + static fromDocument(participantDocument: ParticipantDocument): Participant { + return new Participant( + participantDocument.handle, + participantDocument.profileImage, + participantDocument.snapshot, + participantDocument.point, + participantDocument.goal, + participantDocument.result, + ); + } } export const ParticipantSchema = SchemaFactory.createForClass(Participant); diff --git a/src/entities/poker.entity.ts b/src/entities/poker.entity.ts index 6f9e736..e990f97 100644 --- a/src/entities/poker.entity.ts +++ b/src/entities/poker.entity.ts @@ -1,12 +1,15 @@ -import { Prop, SchemaFactory } from '@nestjs/mongoose'; +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; import { Participant } from './participant.entity'; +export type PokerDocument = HydratedDocument; + +@Schema() export class Poker { - @Prop() id: string; - @Prop() + @Prop({ required: true }) name: string; @Prop() @@ -15,8 +18,26 @@ export class Poker { @Prop() startTime: Date; - @Prop() + @Prop({ required: true }) endTime: Date; + + constructor(id: string, name: string, participants: Participant[], startTime: Date, endTime: Date) { + this.id = id; + this.name = name; + this.participants = participants; + this.startTime = startTime; + this.endTime = endTime; + } + + static fromDocument(pokerDocument: PokerDocument): Poker { + return new Poker( + pokerDocument._id.toString(), + pokerDocument.name, + pokerDocument.participants, + pokerDocument.startTime, + pokerDocument.endTime, + ); + } } export const PokerSchema = SchemaFactory.createForClass(Poker); diff --git a/src/entities/problem.entity.ts b/src/entities/problem.entity.ts index d2d64a4..5865052 100644 --- a/src/entities/problem.entity.ts +++ b/src/entities/problem.entity.ts @@ -1,14 +1,22 @@ -import { SchemaFactory } from '@nestjs/mongoose'; +import { Schema, SchemaFactory } from '@nestjs/mongoose'; +import { HydratedDocument } from 'mongoose'; +export type ProblemDocument = HydratedDocument; + +@Schema() export class Problem { problemId: number; titleKo: string; level: number; - constructor(problem: { problemId: number; titleKo: string; level: number }) { - this.problemId = problem.problemId; - this.titleKo = problem.titleKo; - this.level = problem.level; + constructor(problemId: number, titleKo: string, level: number) { + this.problemId = problemId; + this.titleKo = titleKo; + this.level = level; + } + + static fromDocument(problemDocument: ProblemDocument): Problem { + return new Problem(problemDocument.problemId, problemDocument.titleKo, problemDocument.level); } } diff --git a/src/modules/group/repository.ts b/src/modules/group/repository.ts index a223fd6..c3bb3bd 100644 --- a/src/modules/group/repository.ts +++ b/src/modules/group/repository.ts @@ -11,26 +11,26 @@ export class GroupRepository { private readonly groupModel: Model, ) {} - create(createGroupDto: CreateGroupDto): Promise { - return this.groupModel.create(createGroupDto); + async create(createGroupDto: CreateGroupDto): Promise { + return Group.fromDocument(await this.groupModel.create(createGroupDto)); } - getAll(): Promise { - return this.groupModel.find().exec(); + async getAll(): Promise { + const groupDocs = await this.groupModel.find().exec(); + if (!groupDocs) { + throw new NotFoundException('No groups found'); + } + + return groupDocs.map((document) => Group.fromDocument(document)); } async get(groupId: string): Promise { - const group = await this.groupModel.findById(groupId); - if (!group) { + const groupDoc = await this.groupModel.findById(groupId); + if (!groupDoc) { throw new NotFoundException(`Group not found: ${groupId}`); } - return group; - } - validate(groupId: string, group: Group): void { - if (!group) { - throw new NotFoundException(`Group not found: ${groupId}`); - } + return Group.fromDocument(groupDoc); } deleteAll() { @@ -43,6 +43,6 @@ export class GroupRepository { throw new NotFoundException(`Group not found: ${groupId}`); } - return updatedGroup; + return Group.fromDocument(updatedGroup); } } diff --git a/src/modules/poker/controller.ts b/src/modules/poker/controller.ts index 7d17439..8a72f55 100644 --- a/src/modules/poker/controller.ts +++ b/src/modules/poker/controller.ts @@ -1,5 +1,5 @@ -import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common'; -import { ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; +import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; +import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; import { CreatePokerDto } from './dto/create-poker.dto'; import { PokerService } from './service'; @@ -11,12 +11,8 @@ export class PokerController { @Post() @ApiOperation({ summary: '포커 생성' }) - @ApiQuery({ - name: 'name', - description: '포커 이름', - }) - async create(@Query('name') name: string, @Body() createPokerDto: CreatePokerDto) { - return this.pokerService.create(name, createPokerDto); + async create(@Body() createPokerDto: CreatePokerDto) { + return this.pokerService.create(createPokerDto); } @Get() diff --git a/src/modules/poker/dto/create-poker.dto.ts b/src/modules/poker/dto/create-poker.dto.ts index 5d0c275..ace06e7 100644 --- a/src/modules/poker/dto/create-poker.dto.ts +++ b/src/modules/poker/dto/create-poker.dto.ts @@ -10,6 +10,12 @@ export class CreatePokerDto { }) groupId: string; + @ApiProperty({ + description: '포커 이름', + default: 'SCCC 포커', + }) + name: string; + @ApiProperty({ description: '게임에 참여하는 사람들의 목록', default: [ diff --git a/src/modules/poker/repository.ts b/src/modules/poker/repository.ts index f044066..8dab500 100644 --- a/src/modules/poker/repository.ts +++ b/src/modules/poker/repository.ts @@ -1,47 +1,49 @@ +import { Participant } from '@entities/participant.entity'; import { Poker } from '@entities/poker.entity'; +import { CreatePokerDto } from '@modules/poker/dto/create-poker.dto'; import { Inject, Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; -import { GroupRepository } from '../group/repository'; - @Injectable() export class PokerRepository { - constructor( - @Inject('POKER_MODEL') private readonly pokerModel: Model, - private readonly groupRepository: GroupRepository, - ) {} - - create(poker: Poker): Promise { - const createdPoker = new this.pokerModel(poker); - return createdPoker.save(); + constructor(@Inject('POKER_MODEL') private readonly pokerModel: Model) {} + + async create(createPokerDto: CreatePokerDto, participants: Participant[]): Promise { + const pokerDoc = new this.pokerModel({ + name: createPokerDto.name, + participants: participants, + startTime: new Date(), + endTime: createPokerDto.endDate, + }); + + return Poker.fromDocument(await pokerDoc.save()); } - getAll(): Promise { - return this.pokerModel.find(); + async getAll(): Promise { + const pokerDocs = await this.pokerModel.find().exec(); + if (!pokerDocs) { + throw new Error('No pokers found'); + } + + return pokerDocs.map((document) => Poker.fromDocument(document)); } async get(id: string): Promise { - const poker = (await this.pokerModel.findById(id)) as Poker; - if (!poker) { + const pokerDoc = await this.pokerModel.findById(id); + if (!pokerDoc) { throw new Error(`Poker not found: ${id.toString()}`); } - return poker; + return Poker.fromDocument(pokerDoc); } - validate(id: string, poker: Poker): void { - if (!poker) { + async update(id: string, poker: Poker): Promise { + const pokerDoc = await this.pokerModel.findByIdAndUpdate(id, poker); + if (!pokerDoc) { throw new Error(`Poker not found: ${id}`); } - } - update(id: string, poker: Poker): void { - this.pokerModel.updateOne( - { - _id: id, - }, - poker, - ); + return Poker.fromDocument(pokerDoc); } async deleteAll(): Promise { diff --git a/src/modules/poker/service.ts b/src/modules/poker/service.ts index c1a7a28..08eb2b5 100644 --- a/src/modules/poker/service.ts +++ b/src/modules/poker/service.ts @@ -1,8 +1,8 @@ +import { Participant } from '@entities/participant.entity'; import { Poker } from '@entities/poker.entity'; +import { GroupRepository } from '@modules/group/repository'; import { Injectable } from '@nestjs/common'; -import { ObjectId } from 'mongodb'; -import { GroupRepository } from '../group/repository'; import { ProblemService } from '../problem/service'; import { UserService } from '../user/service'; import { CreatePokerDto } from './dto/create-poker.dto'; @@ -17,25 +17,17 @@ export class PokerService { private readonly groupRepository: GroupRepository, ) {} - async create(name: string, createPokerDto: CreatePokerDto): Promise { + async create(createPokerDto: CreatePokerDto): Promise { const group = await this.groupRepository.get(createPokerDto.groupId); - this.groupRepository.validate(createPokerDto.groupId, group); - - const poker: Poker = { - id: new ObjectId().toString(), - name, - participants: [], - startTime: new Date(), - endTime: createPokerDto.endDate, - }; + const participants: Participant[] = []; for (const participant of createPokerDto.participants) { const handle = participant.handle; const goal = participant.goal; const profileImage = await this.userService.getProfileImageFromSolved(handle); const snapshot = await this.userService.getProblemsFromBoj(handle); - poker.participants.push({ + participants.push({ handle, profileImage, snapshot, @@ -45,12 +37,12 @@ export class PokerService { }); } - const createdPoker = await this.pokerRepository.create(poker); - const pokerId = createdPoker.id; - group.pokers.push(pokerId); + const createdPoker = await this.pokerRepository.create(createPokerDto, participants); + + group.pokers.push(createdPoker.id); await this.groupRepository.update(createPokerDto.groupId, group); - return poker; + return createdPoker; } async getAll(): Promise { @@ -92,7 +84,6 @@ export class PokerService { async refresh(pokerId: string) { const poker = await this.pokerRepository.get(pokerId); - this.pokerRepository.validate(pokerId, poker); for (const participant of poker.participants) { const snapshot = participant.snapshot; diff --git a/src/modules/problem/service.ts b/src/modules/problem/service.ts index 01f6a06..b1bb761 100644 --- a/src/modules/problem/service.ts +++ b/src/modules/problem/service.ts @@ -40,7 +40,8 @@ export class ProblemService { .then((res) => res.data); for (const problem of response['items']) { - problems.push(new Problem(problem)); + const { problemId, titleKo, level } = problem; + problems.push(new Problem(problemId, titleKo, level)); } }