Skip to content

Commit

Permalink
feat: Epic, Task, Story 생성, 수정시 중복된 rankValue를 서버가 조정해, 조정된 rankValue…
Browse files Browse the repository at this point in the history
…를 반환하는 로직 구현

- Entity
  - 유니크 제약조건 이름 명시
- Repository
  - Create 메서드를 Unique오류이며, RankValue중복일 경우 커스텀 에러를 throw하게 변경
  - Update 메서드를 Unique오류이며, RankValue중복일 경우 커스텀 에러를 throw하게 변경
  - getNext[Epic/Task/Story]ByRankValue 메서드 구현
    - 특정 rankValue값 다음 순서의 rankValue를 가진 엔티티 반환
- Controller
  - update시 조정된 rankValue값을 반영해 유저에게 반환하도록 수정
- Test
  - Epic/Task/Story 생성 시 동시에 중복된 rankValue를 전송했을때 조정되는것 테스트
  - Epic/Task/Story 변경 시 동시에 중복된 rankValue를 전송했을때 조정되는것 테스트
  • Loading branch information
choyoungwoo9 committed Aug 2, 2024
1 parent 268d63e commit 45d8057
Show file tree
Hide file tree
Showing 11 changed files with 757 additions and 90 deletions.
2 changes: 1 addition & 1 deletion backend/src/project/entity/epic.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export enum EpicColor {
}

@Entity()
@Unique(['rankValue', 'projectId'])
@Unique('EPIC_UQ_RANK_VALUE_AND_PROJECT_ID', ['rankValue', 'projectId'])
export class Epic {
@PrimaryGeneratedColumn('increment', { type: 'int' })
id: number;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/project/entity/story.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export enum StoryStatus {
}

@Entity()
@Unique(['rankValue', 'epicId'])
@Unique('STORY_UQ_RANK_VALUE_AND_EPIC_ID', ['rankValue', 'epicId'])
export class Story {
@PrimaryGeneratedColumn('increment', { type: 'int' })
id: number;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/project/entity/task.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export enum TaskStatus {
}

@Entity()
@Unique(['rankValue', 'storyId'])
@Unique('TASK_UQ_RANK_VALUE_AND_STORY_ID', ['rankValue', 'storyId'])
export class Task {
@PrimaryGeneratedColumn('increment', { type: 'int' })
id: number;
Expand Down
130 changes: 108 additions & 22 deletions backend/src/project/project.repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Connection, DataSource, MoreThan, Repository } from 'typeorm';
import { Injectable } from '@nestjs/common';
import { Project } from './entity/project.entity';
import { ProjectToMember } from './entity/project-member.entity';
Expand All @@ -9,6 +9,7 @@ import { Link } from './entity/link.entity.';
import { Epic, EpicColor } from './entity/epic.entity';
import { Story, StoryStatus } from './entity/story.entity';
import { Task, TaskStatus } from './entity/task.entity';
import { LexoRank } from 'lexorank';

@Injectable()
export class ProjectRepository {
Expand All @@ -29,6 +30,7 @@ export class ProjectRepository {
private readonly storyRepository: Repository<Story>,
@InjectRepository(Task)
private readonly taskRepository: Repository<Task>,
private readonly dataSource: DataSource,
) {}

create(project: Project): Promise<Project> {
Expand Down Expand Up @@ -117,8 +119,27 @@ export class ProjectRepository {
return result.affected ? result.affected : 0;
}

createEpic(epic: Epic): Promise<Epic> {
return this.epicRepository.save(epic);
async createEpic(epic: Epic): Promise<Epic> {
try {
return await this.epicRepository.save(epic);
} catch (e) {
if (
e.code === 'ER_DUP_ENTRY' &&
e.sqlMessage.includes('EPIC_UQ_RANK_VALUE_AND_PROJECT_ID')
)
throw new Error('DUPLICATED RANK VALUE');
throw e;
}
}

getNextEpicByRankValue(projectId: number, rankValue: string) {
return this.epicRepository.findOne({
where: {
projectId,
rankValue: MoreThan(rankValue),
},
order: { rankValue: 'ASC' },
});
}

async deleteEpic(project: Project, epicId: number): Promise<number> {
Expand Down Expand Up @@ -149,11 +170,20 @@ export class ProjectRepository {
updateData.rankValue = rankValue;
}

const result = await this.epicRepository.update(
{ id, project: { id: project.id } },
updateData,
);
return !!result.affected;
try {
const result = await this.epicRepository.update(
{ id, project: { id: project.id } },
updateData,
);
return !!result.affected;
} catch (e) {
if (
e.code === 'ER_DUP_ENTRY' &&
e.sqlMessage.includes('EPIC_UQ_RANK_VALUE_AND_PROJECT_ID')
)
throw new Error('DUPLICATED RANK VALUE');
throw e;
}
}

getEpicById(project: Project, id: number) {
Expand All @@ -162,8 +192,27 @@ export class ProjectRepository {
});
}

createStory(story: Story): Promise<Story> {
return this.storyRepository.save(story);
async createStory(story: Story): Promise<Story> {
try {
return await this.storyRepository.save(story);
} catch (e) {
if (
e.code === 'ER_DUP_ENTRY' &&
e.sqlMessage.includes('STORY_UQ_RANK_VALUE_AND_EPIC_ID')
)
throw new Error('DUPLICATED RANK VALUE');
throw e;
}
}

getNextStoryByRankValue(epicId: number, rankValue: string) {
return this.storyRepository.findOne({
where: {
epicId,
rankValue: MoreThan(rankValue),
},
order: { rankValue: 'ASC' },
});
}

async deleteStory(project: Project, storyId: number): Promise<number> {
Expand Down Expand Up @@ -201,11 +250,20 @@ export class ProjectRepository {
updateData.rankValue = rankValue;
}

const result = await this.storyRepository.update(
{ id, project: { id: project.id } },
updateData,
);
return !!result.affected;
try {
const result = await this.storyRepository.update(
{ id, project: { id: project.id } },
updateData,
);
return !!result.affected;
} catch (e) {
if (
e.code === 'ER_DUP_ENTRY' &&
e.sqlMessage.includes('STORY_UQ_RANK_VALUE_AND_EPIC_ID')
)
throw new Error('DUPLICATED RANK VALUE');
throw e;
}
}

getStoryById(project: Project, id: number) {
Expand All @@ -224,8 +282,27 @@ export class ProjectRepository {
return targetProject.displayIdCount;
}

async createTask(task: Task) {
return this.taskRepository.save(task);
async createTask(task: Task): Promise<Task> {
try {
return await this.taskRepository.save(task);
} catch (e) {
if (
e.code === 'ER_DUP_ENTRY' &&
e.sqlMessage.includes('TASK_UQ_RANK_VALUE_AND_STORY_ID')
)
throw new Error('DUPLICATED RANK VALUE');
throw e;
}
}

getNextTaskByRankValue(storyId: number, rankValue: string) {
return this.taskRepository.findOne({
where: {
storyId,
rankValue: MoreThan(rankValue),
},
order: { rankValue: 'ASC' },
});
}

async deleteTask(project: Project, taskId: number): Promise<number> {
Expand Down Expand Up @@ -271,11 +348,20 @@ export class ProjectRepository {
updateData.rankValue = rankValue;
}

const result = await this.taskRepository.update(
{ id, project: { id: project.id } },
updateData,
);
return !!result.affected;
try {
const result = await this.taskRepository.update(
{ id, project: { id: project.id } },
updateData,
);
return !!result.affected;
} catch (e) {
if (
e.code === 'ER_DUP_ENTRY' &&
e.sqlMessage.includes('TASK_UQ_RANK_VALUE_AND_STORY_ID')
)
throw new Error('DUPLICATED RANK VALUE');
throw e;
}
}

getProjectBacklog(project: Project) {
Expand Down
Loading

0 comments on commit 45d8057

Please sign in to comment.