Skip to content

Commit

Permalink
feat: /group invite, changeRole
Browse files Browse the repository at this point in the history
  • Loading branch information
w8385 committed Jun 5, 2024
1 parent 3205cdc commit ccb3f3a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 24 deletions.
41 changes: 28 additions & 13 deletions src/group/group.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import {
Get,
Param,
ParseArrayPipe,
ParseEnumPipe,
Patch,
Post,
Query,
Req,
UploadedFile,
UseGuards,
} from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam } from '@nestjs/swagger';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiQuery } from '@nestjs/swagger';

import { AuthGuard } from '../auth/auth.guard';
import { ApiFile } from '../common/decorators/api-file.decorator';
Expand Down Expand Up @@ -329,31 +330,45 @@ export class GroupController {
return this.groupService.deleteEvent(req.userId, groupId, eventId);
}

@Post(':groupId/invite/editor')
@Post(':groupId/user/:userId')
@User()
@ApiOperation({
summary: '모임 편집자 초대',
description: '특정 모임에 편집자를 초대합니다.',
summary: '사용자 모임 초대',
description: '특정 모임에 사용자를 초대합니다.',
})
inviteEditor(
@ApiQuery({
name: 'role',
required: true,
description: '권한',
enum: ['editor', 'viewer'],
})
invite(
@Req() req: any,
@Param('groupId', new ParseObjectIdPipe()) groupId: string,
@Query('userId', new ParseObjectIdPipe()) userId: string,
@Param('userId', new ParseObjectIdPipe()) userId: string,
@Query('role', new ParseEnumPipe({ enum: ['editor', 'viewer'] })) role: string,
) {
return this.groupService.inviteEditor(req.userId, groupId, userId);
return this.groupService.invite(req.userId, groupId, userId, role);
}

@Post(':groupId/invite/viewer')
@Patch(':groupId/user/:userId')
@User()
@ApiOperation({
summary: '모임 조회자 초대',
description: '특정 모임에 조회자를 초대합니다.',
summary: '모임 사용자 권한 변경',
description: '특정 모임의 관리자 사용자의 권한을 변경합니다.',
})
@ApiQuery({
name: 'role',
required: true,
description: '권한',
enum: ['editor', 'viewer'],
})
inviteViewer(
changeRole(
@Req() req: any,
@Param('groupId', new ParseObjectIdPipe()) groupId: string,
@Query('userId', new ParseObjectIdPipe()) userId: string,
@Param('userId', new ParseObjectIdPipe()) userId: string,
@Query('role', new ParseEnumPipe({ enum: ['editor', 'viewer'] })) role: string,
) {
return this.groupService.inviteViewer(req.userId, groupId, userId);
return this.groupService.changeRole(req.userId, groupId, userId, role);
}
}
77 changes: 75 additions & 2 deletions src/group/group.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class GroupService {

async addMember(userId: string, groupId: string, createMemberDto: CreateMemberDto[]): Promise<Group> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

for (const member of createMemberDto) {
Expand All @@ -60,6 +61,7 @@ export class GroupService {

async addMemberToEvent(userId: string, groupId: string, eventId: string, memberIds: string[]) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

const event = await this.eventService.getOne(eventId);
Expand All @@ -78,6 +80,7 @@ export class GroupService {

async uploadMemberFile(userId: string, groupId: string, memberExcel: Express.Multer.File): Promise<Group> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

group.members = await this.memberService.uploadMemberFile(memberExcel);
Expand All @@ -99,6 +102,7 @@ export class GroupService {

async addEvent(userId: string, groupId: string, createEventDto: CreateEventDto): Promise<object> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

const eventId = await this.eventService.create(createEventDto);
Expand All @@ -118,6 +122,7 @@ export class GroupService {

async getOne(userId: string, groupId: string): Promise<Group> {
const group: Group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

const members = await this.getAllMembers(group.members);
Expand All @@ -131,6 +136,7 @@ export class GroupService {

async getMembers(userId: string, groupId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

const members = await this.getAllMembers(group.members);
Expand All @@ -146,41 +152,47 @@ export class GroupService {

async getEvents(userId: string, groupId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

return await this.getAllEvents(group.events);
}

async getEvent(userId: string, groupId: string, eventId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

return this.eventService.getOne(eventId);
}

async getTransactions(userId: string, groupId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

return this.transactionService.getTransactions(groupId);
}

async getTransactionsByEvent(userId: string, groupId: string, eventId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

return this.eventService.compareEventTransactions(groupId, eventId);
}

async getTransactionsByPeriod(userId: string, groupId: string, periodDto: GetTransactionsPeriodDto) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, userId);

return this.transactionService.getTransactionsByPeriod(groupId, periodDto.startDate, periodDto.endDate);
}

async update(userId: string, groupId: string, updateGroupDto: UpdateGroupDto): Promise<Group> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

return await this.groupRepository.update(groupId, {
Expand All @@ -191,6 +203,7 @@ export class GroupService {

async updateMember(userId: string, groupId: string, memberId: string, updateMemberDto: UpdateMemberDto) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

const member = await this.memberService.update(memberId, updateMemberDto);
Expand All @@ -204,6 +217,7 @@ export class GroupService {

async updateEvent(userId: string, groupId: string, eventId: string, updateEventDto: UpdateEventDto) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

const event = await this.eventService.update(eventId, updateEventDto);
Expand All @@ -217,13 +231,15 @@ export class GroupService {

async delete(userId: string, groupId: string): Promise<void> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupOwner(group, userId);

return this.groupRepository.delete(groupId);
}

async deleteMembers(userId: string, groupId: string, memberIds: string[]): Promise<void> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

const groupMembers = group.members;
Expand All @@ -244,6 +260,7 @@ export class GroupService {

async deleteEvent(userId: string, groupId: string, eventId: string): Promise<void> {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupEditor(group, userId);

const groupEvents = group.events;
Expand All @@ -254,8 +271,29 @@ export class GroupService {
await this.groupRepository.update(groupId, group);
}

async inviteEditor(senderId: string, groupId: string, receiverId: string) {
async invite(senderId: string, groupId: string, receiverId: string, role: string) {
if (role === 'editor') {
return await this.inviteEditor(senderId, groupId, receiverId);
} else if (role === 'viewer') {
return await this.inviteViewer(senderId, groupId, receiverId);
} else {
throw new Error('유효하지 않은 권한입니다.');
}
}

async changeRole(senderId: string, groupId: string, receiverId: string, role: string) {
if (role === 'editor') {
return await this.changeToEditor(senderId, groupId, receiverId);
} else if (role === 'viewer') {
return await this.changeToViewer(senderId, groupId, receiverId);
} else {
throw new Error('유효하지 않은 권한입니다.');
}
}

private async inviteEditor(senderId: string, groupId: string, receiverId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupOwner(group, senderId);
this.groupValidator.validateGroupInvite(group, receiverId);

Expand All @@ -265,8 +303,9 @@ export class GroupService {
return await this.groupRepository.update(groupId, group);
}

async inviteViewer(senderId: string, groupId: string, receiverId: string) {
private async inviteViewer(senderId: string, groupId: string, receiverId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupViewer(group, senderId);
this.groupValidator.validateGroupInvite(group, receiverId);

Expand All @@ -276,6 +315,40 @@ export class GroupService {
return await this.groupRepository.update(groupId, group);
}

private async changeToEditor(senderId: string, groupId: string, receiverId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupOwner(group, senderId);

if (!group.auth.viewers.includes(receiverId)) {
throw new Error('대상이 조회자가 아닙니다.');
}

if (group.auth.editors.includes(receiverId)) {
throw new Error('이미 편집자입니다.');
}

group.auth.viewers = group.auth.viewers.filter((viewer) => viewer !== receiverId);
group.auth.editors.push(receiverId);
}

private async changeToViewer(senderId: string, groupId: string, receiverId: string) {
const group = await this.groupRepository.findOne(groupId);
this.groupValidator.validateGroup(group);
this.groupValidator.validateGroupOwner(group, senderId);

if (!group.auth.editors.includes(receiverId)) {
throw new Error('대상이 편집자가 아닙니다.');
}

if (group.auth.viewers.includes(receiverId)) {
throw new Error('이미 조회자입니다.');
}

group.auth.editors = group.auth.editors.filter((editor) => editor !== receiverId);
group.auth.viewers.push(receiverId);
}

private async getAllMembers(memberIds: string[]) {
const members = [];
for (const memberId of memberIds) {
Expand Down
10 changes: 1 addition & 9 deletions src/group/group.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,24 @@ import { Group } from './entities/group.entity';
@Injectable()
export class GroupValidator {
validateGroupOwner(group: Group, userId: string) {
this.validateGroup(group);

if (group.auth.owner !== userId) {
throw new UnauthorizedException('소유자 권한이 없습니다.');
}
}

validateGroupEditor(group: Group, userId: string) {
this.validateGroup(group);

if (group.auth.owner !== userId && !group.auth.editors.includes(userId)) {
throw new UnauthorizedException('편집 권한이 없습니다.');
}
}

validateGroupViewer(group: Group, userId: string) {
this.validateGroup(group);

if (group.auth.owner !== userId && !group.auth.editors.includes(userId) && !group.auth.viewers.includes(userId)) {
throw new UnauthorizedException('조회 권한이 없습니다.');
}
}

validateGroupInvite(group: Group, receiverId: string) {
this.validateGroup(group);

if (
group.auth.owner === receiverId ||
group.auth.editors.includes(receiverId) ||
Expand All @@ -40,7 +32,7 @@ export class GroupValidator {
}
}

private validateGroup(group: Group) {
validateGroup(group: Group) {
if (!group) {
throw new NotFoundException('모임을 찾을 수 없습니다.');
}
Expand Down

0 comments on commit ccb3f3a

Please sign in to comment.