Skip to content

Commit

Permalink
feat: 채널/DM 생성 API 분리 (#103)
Browse files Browse the repository at this point in the history
* refactor: 소스코드 내 상수를 enum으로 관리

- chat-types enum 추가 및 적용
- default-section-name, http-status-code를 enum으로 변경 및 적용
- 코드 상의 상수를 enum으로 관리하도록 수정

* feat: 채널/DM 생성 API 분리

- 채널/DM 생성 API를 채널 생성 API와 DM 생성 API로 분리
  • Loading branch information
profornnan authored Dec 3, 2020
1 parent f3de071 commit f97d34a
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 54 deletions.
5 changes: 5 additions & 0 deletions server/src/common/constants/chat-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const enum ChatType {
DM = 'DM',
Channel = 'Channel'
}
export default ChatType;
9 changes: 5 additions & 4 deletions server/src/common/constants/default-section-name.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const DefaultSectionName = {
Channels: 'Channels',
DirectMessages: 'DirectMessages'
};
const enum DefaultSectionName {
Channels = 'Channels',
DirectMessages = 'Direct Messages',
Starred = 'Starred'
}

export default DefaultSectionName;
22 changes: 11 additions & 11 deletions server/src/common/constants/http-status-code.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const HttpStatusCode = {
OK: 200,
CREATED: 201,
NO_CONTENT: 204,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
CONFLICT: 409,
INTERNAL_SERVER_ERROR: 500
};
const enum HttpStatusCode {
OK = 200,
CREATED = 201,
NO_CONTENT = 204,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
CONFLICT = 409,
INTERNAL_SERVER_ERROR = 500
}

export default HttpStatusCode;
18 changes: 14 additions & 4 deletions server/src/controller/chatroom-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@ import ChatroomService from '@service/chatroom-service';
import { NextFunction, Request, Response } from 'express';

const ChatroomController = {
async createChatroom(req: Request, res: Response, next: NextFunction) {
async createChannel(req: Request, res: Response, next: NextFunction) {
try {
const { userId } = req.user;
const { title, description, isPrivate, chatType } = req.body;
await ChatroomService.getInstance().createChatroom({ userId, title, description, isPrivate, chatType });
res.status(HttpStatusCode.CREATED).send();
const { title, description, isPrivate } = req.body;
const chatroomId = await ChatroomService.getInstance().createChannel({ userId: Number(userId), title, description, isPrivate });
res.status(HttpStatusCode.CREATED).json({ chatroomId });
} catch (err) {
next(err);
}
},
async createDM(req: Request, res: Response, next: NextFunction) {
try {
const { userId } = req.user;
const { invitedUserId } = req.body;
const chatroomId = await ChatroomService.getInstance().createDM(Number(userId), Number(invitedUserId));
res.status(HttpStatusCode.CREATED).json({ chatroomId });
} catch (err) {
next(err);
}
Expand Down
3 changes: 2 additions & 1 deletion server/src/model/chatroom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateCol
import { IsBoolean, IsIn, IsString } from 'class-validator';
import UserChatroom from '@model/user-chatroom';
import Message from '@model/message';
import ChatType from '@constants/chat-type';

@Entity({ name: 'chatroom' })
export default class Chatroom {
Expand All @@ -20,7 +21,7 @@ export default class Chatroom {
isPrivate: boolean;

@Column()
@IsIn(['DM', 'Channel'])
@IsIn([ChatType.DM, ChatType.Channel])
chatType: string;

@Column({ nullable: true })
Expand Down
3 changes: 2 additions & 1 deletion server/src/router/chatroom-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import ChatroomController from '@controller/chatroom-controller';

const router = express.Router();

router.post('/', ChatroomController.createChatroom);
router.post('/channel', ChatroomController.createChannel);
router.post('/dm', ChatroomController.createDM);
router.get('/:chatroomId', ChatroomController.getChatroomInfo);
router.patch('/:chatroomId', ChatroomController.updateChatroom);
router.delete('/:chatroomId', ChatroomController.deleteChatroom);
Expand Down
5 changes: 3 additions & 2 deletions server/src/seeds/chatroom.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Factory, Seeder } from 'typeorm-seeding';
import { Connection } from 'typeorm';
import Chatroom from '../model/chatroom';
import ChatType from '@constants/chat-type';
import Chatroom from '@model/chatroom';

export default class CreateChatrooms implements Seeder {
public async run(factory: Factory, connection: Connection): Promise<any> {
const chatroomData = [
{
title: 'random',
isPrivate: false,
chatType: 'Channel'
chatType: ChatType.Channel
}
];
const res = await connection.getRepository(Chatroom).findOne(chatroomData[0]);
Expand Down
84 changes: 58 additions & 26 deletions server/src/service/chatroom-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,12 @@ import { Transactional } from 'typeorm-transactional-cls-hooked';
import ChatroomRepository from '@repository/chatroom-repository';
import UserRepository from '@repository/user-repository';
import UserChatroomRepository from '@repository/user-chatroom-repository';
import User from '@model/user';
import Chatroom from '@model/chatroom';
import validator from '@utils/validator';
import BadRequestError from '@error/bad-request-error';
import NotFoundError from '@error/not-found-error';

interface saveChatroomParams {
title: string;
description: string;
isPrivate: boolean;
chatType: 'DM' | 'Channel';
}

interface createChatroomParams extends saveChatroomParams {
userId: number;
}

interface saveUserChatroomParams {
sectionName: string;
user: User;
chatroom: Chatroom;
}
import ChatType from '@constants/chat-type';
import DefaultSectionName from '@constants/default-section-name';
import ConflictError from '@error/conflict-error';

class ChatroomService {
static instance: ChatroomService;
Expand All @@ -49,27 +33,75 @@ class ChatroomService {
}

@Transactional()
async createChatroom({ userId, title, description, isPrivate, chatType }: createChatroomParams) {
async createChannel({ userId, title, description, isPrivate }) {
const user = await this.userRepository.findOne(userId);
const chatroom = await this.chatroomRepository.findByTitle(title);
const sectionName = chatType === 'DM' ? 'Direct Message' : 'Channels';

if (chatroom || !user) {
throw new BadRequestError();
}

const newChatroom = await this.saveChatroom({ title, description, isPrivate, chatType });
await this.saveUserChatroom({ sectionName, user, chatroom: newChatroom });
const newChatroom = await this.saveChatroom({ title, description, isPrivate, chatType: ChatType.Channel });
await this.saveUserChatroom({ sectionName: DefaultSectionName.Channels, user, chatroom: newChatroom });
return newChatroom.chatroomId;
}

@Transactional()
async createDM(userId: number, invitedUserId: number) {
const user = await this.userRepository.findOne(userId);
const invitedUser = await this.userRepository.findOne(invitedUserId);

if (!user || !invitedUser) {
throw new BadRequestError();
}

await this.validateExistsDM(userId, invitedUserId);

const newChatroom = await this.saveChatroom({ title: null, description: null, isPrivate: true, chatType: ChatType.DM });
await this.saveUserChatroom({ sectionName: DefaultSectionName.DirectMessages, user, chatroom: newChatroom });
await this.saveUserChatroom({ sectionName: DefaultSectionName.DirectMessages, user: invitedUser, chatroom: newChatroom });
return newChatroom.chatroomId;
}

private async validateExistsDM(userId: number, invitedUserId: number) {
const userChatrooms = await this.userChatroomRepository
.createQueryBuilder('userChatroom')
.leftJoinAndSelect('userChatroom.chatroom', 'chatroom')
.where('userChatroom.user.userId = :userId', { userId })
.andWhere('chatroom.chatType = :chatType', { chatType: ChatType.DM })
.getMany();

await Promise.all(
userChatrooms.map(async (userChatroom) => {
const { chatroomId } = userChatroom.chatroom;
const otherUserChatroom = await this.userChatroomRepository
.createQueryBuilder('userChatroom')
.leftJoinAndSelect('userChatroom.chatroom', 'chatroom')
.leftJoinAndSelect('userChatroom.user', 'user')
.where('chatroom.chatroomId = :chatroomId', { chatroomId })
.andWhere('userChatroom.user.userId != :userId', { userId })
.andWhere('chatroom.chatType = :chatType', { chatType: ChatType.DM })
.getOne();

if (otherUserChatroom && otherUserChatroom.user.userId === invitedUserId) {
throw new ConflictError();
}
})
);
}

private async saveChatroom({ title, description, isPrivate, chatType }: saveChatroomParams) {
private async saveChatroom({ title, description, isPrivate, chatType }) {
const chatroom = this.chatroomRepository.create({ title, description, isPrivate, chatType });
await validator(chatroom);

if (chatType === ChatType.Channel) {
await validator(chatroom);
}

const newChatroom = await this.chatroomRepository.save(chatroom);
return newChatroom;
}

private async saveUserChatroom({ sectionName, user, chatroom }: saveUserChatroomParams) {
private async saveUserChatroom({ sectionName, user, chatroom }) {
const userChatroom = this.userChatroomRepository.create({ sectionName, user, chatroom });
await validator(userChatroom);
const newUserChatroom = await this.userChatroomRepository.save(userChatroom);
Expand Down
10 changes: 6 additions & 4 deletions server/src/service/user-chatroom-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import UserRepository from '@repository/user-repository';
import ChatroomRepository from '@repository/chatroom-repository';
import BadRequestError from '@error/bad-request-error';
import validator from '@utils/validator';
import ChatType from '@constants/chat-type';
import DefaultSectionName from '@constants/default-section-name';

class UserChatroomService {
static instance: UserChatroomService;
Expand Down Expand Up @@ -45,9 +47,9 @@ class UserChatroomService {
throw new NotFoundError();
}

const starred = this.classifyChannelsBySectionName(userChatrooms, 'Starred');
const starred = this.classifyChannelsBySectionName(userChatrooms, DefaultSectionName.Starred);
const otherSections = await this.classifyOtherSections(userChatrooms, userId);
const channels = this.classifyChannelsBySectionName(userChatrooms, 'Channels');
const channels = this.classifyChannelsBySectionName(userChatrooms, DefaultSectionName.Channels);
const directMessages = await this.classifyDirectMessages(userChatrooms, userId);

return { starred, otherSections, channels, directMessages };
Expand Down Expand Up @@ -81,7 +83,7 @@ class UserChatroomService {
private async classifyDirectMessages(userChatrooms: any[], userId: number) {
const directMessages = await Promise.all(
userChatrooms
.filter((userChatroom) => userChatroom.sectionName === 'Direct Message')
.filter((userChatroom) => userChatroom.sectionName === DefaultSectionName.DirectMessages)
.map(async (userChatroom) => {
const { chatroomId, chatType } = userChatroom.chatroom;
const { title, chatProfileImg } = await this.findTitleAndImg(userChatroom, userId);
Expand Down Expand Up @@ -175,7 +177,7 @@ class UserChatroomService {
}

private async saveChatroom(user, chatroom) {
const sectionName = chatroom.chatType === 'DM' ? 'Direct Message' : 'Channels';
const sectionName = chatroom.chatType === ChatType.DM ? DefaultSectionName.DirectMessages : DefaultSectionName.Channels;
const newUserChatroom = this.userChatroomRepository.create({ user, chatroom, sectionName });
await validator(newUserChatroom);
await this.userChatroomRepository.save(newUserChatroom);
Expand Down
3 changes: 2 additions & 1 deletion server/src/service/user-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import NotFoundError from '@error/not-found-error';
import Chatroom from '@model/chatroom';
import User from '@model/user';
import DefaultSectionName from '@constants/default-section-name';
import ChatType from '@constants/chat-type';

class UserService {
static instance: UserService;
Expand Down Expand Up @@ -76,7 +77,7 @@ class UserService {
}

private async createDirectMessage() {
const newDirectMessage = await this.chatroomRepository.save({ isPrivate: true, chatType: 'DM' });
const newDirectMessage = await this.chatroomRepository.save({ isPrivate: true, chatType: ChatType.DM });
return newDirectMessage;
}

Expand Down

0 comments on commit f97d34a

Please sign in to comment.