-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] 프로젝트 생성 API 구현 #55
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { CreateDateColumn, UpdateDateColumn } from 'typeorm'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 모든 엔티티에 필요하다고 생각합니다. 그래서 common에 위치시켰습니다. 딱히 적절한 위치가 생각나지 않더군요.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다. 저는 저렇게 타임스탬프를 분리해본 경험조차 없습니다 ㅎㅎ |
||
|
||
export class EntityTimestamp { | ||
@CreateDateColumn() | ||
createdAt: Date; | ||
|
||
@UpdateDateColumn() | ||
updatedAt: Date; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Body, Controller, Post, UseGuards } from '@nestjs/common'; | ||
import { ProjectService } from '../service/project.service'; | ||
import { AccessTokenGuard } from '@/account/guard/accessToken.guard'; | ||
import { CreateProjectRequest } from '../dto/create-project-request.dto'; | ||
import { AuthUser } from '@/account/decorator/authUser.decorator'; | ||
import { Account } from '@/account/entity/account.entity'; | ||
|
||
@UseGuards(AccessTokenGuard) | ||
@Controller('projects') | ||
export class ProjectController { | ||
constructor(private projectService: ProjectService) {} | ||
|
||
@Post() | ||
create(@AuthUser() user: Account, @Body() body: CreateProjectRequest) { | ||
return this.projectService.create(user.id, body.title); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { IsNotEmpty, IsString, Length } from 'class-validator'; | ||
|
||
export class CreateProjectRequest { | ||
@IsNotEmpty() | ||
@IsString() | ||
@Length(1, 20) | ||
title: string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Project } from '../entity/project.entity'; | ||
|
||
export class CreateProjectResponse { | ||
id: number; | ||
|
||
title: string; | ||
|
||
constructor(project: Project) { | ||
this.id = project.id; | ||
this.title = project.title; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; | ||
import { ContributorStatus } from '../enum/contributor-status.enum'; | ||
import { ProjectRole } from '../enum/project-role.enum'; | ||
import { EntityTimestamp } from '@/common/entity-timestamp.entity'; | ||
|
||
@Entity() | ||
export class Contributor extends EntityTimestamp { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
@Column() | ||
userId: number; | ||
|
||
@Column() | ||
projectId: number; | ||
|
||
@Column({ type: 'enum', enum: ContributorStatus }) | ||
status: ContributorStatus; | ||
|
||
@Column({ type: 'enum', enum: ProjectRole }) | ||
role: ProjectRole; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; | ||
import { EntityTimestamp } from '@/common/entity-timestamp.entity'; | ||
|
||
@Entity() | ||
export class Project extends EntityTimestamp { | ||
@PrimaryGeneratedColumn() | ||
id: number; | ||
|
||
@Column() | ||
title: string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export enum ContributorStatus { | ||
PENDING = 'PENDING', | ||
ACCEPTED = 'ACCEPTED', | ||
REJECTED = 'REJECTED', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export enum ProjectRole { | ||
ADMIN = 'ADMIN', | ||
GUEST = 'GUEST', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { ProjectService } from './service/project.service'; | ||
import { ProjectController } from './controller/project.controller'; | ||
import { Project } from './entity/project.entity'; | ||
import { Contributor } from './entity/contributor.entity'; | ||
|
||
@Module({ | ||
imports: [TypeOrmModule.forFeature([Project, Contributor])], | ||
controllers: [ProjectController], | ||
providers: [ProjectService], | ||
}) | ||
export class ProjectModule {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { InjectRepository } from '@nestjs/typeorm'; | ||
import { Repository } from 'typeorm'; | ||
import { Project } from '../entity/project.entity'; | ||
import { Contributor } from '../entity/contributor.entity'; | ||
import { ContributorStatus } from '../enum/contributor-status.enum'; | ||
import { ProjectRole } from '../enum/project-role.enum'; | ||
import { CreateProjectResponse } from '../dto/create-project-response.dto'; | ||
|
||
@Injectable() | ||
export class ProjectService { | ||
constructor( | ||
@InjectRepository(Project) private projectRepository: Repository<Project>, | ||
@InjectRepository(Contributor) private contributorRepository: Repository<Contributor> | ||
) {} | ||
|
||
async create(userId: number, title: string) { | ||
const project = await this.projectRepository.save({ title }); | ||
await this.contributorRepository.save({ | ||
userId, | ||
projectId: project.id, | ||
status: ContributorStatus.ACCEPTED, | ||
role: ProjectRole.ADMIN, | ||
}); | ||
return new CreateProjectResponse(project); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 깔꼼하군요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 나중에 트랜잭션 처리도 해야겠군요... |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴
username 과 email 이 명확하게 분리되었으면 좋겠습니다.
검증은
@isEmail()
이라 이메일을 요구하는 것 같지만, 이름이 username 이라 조금 당황했습니다...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
서버에서 사용하는
username
은 아이디로 사용되고 있고, 회원가입 때부터 아이디는 이메일 형식으로 받고 있었습니다. 그냥 이메일 형식을 사용하지 않도록 수정해두겠습니다.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 username 과, email 중 선택하면 되는 부분인 것 같아요!
저희가 email 을 사용할 필요가 없다면, username 으로도 충분하다고 생각됩니다.