Skip to content

Commit

Permalink
feat: Group, Member 구현 (#6)
Browse files Browse the repository at this point in the history
* feat: Group CRUD 구현

* refactor: dto 및 스킴의 ObjectId 타입 > string

* fix: group/update SQL Injection issue

* feat: member crud

* fix: member crud npm test

---------

Co-authored-by: ssoxong <[email protected]>
  • Loading branch information
w8385 and ssoxong authored Apr 10, 2024
1 parent dabf131 commit dcb8d5d
Show file tree
Hide file tree
Showing 18 changed files with 285 additions and 54 deletions.
34 changes: 34 additions & 0 deletions src/group/dto/create-group.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,38 @@ export class CreateGroupDto {
example: 'SSU',
})
readonly name: string;

@ApiProperty({
description: '모임 설명',
example: '숭실대학교 학생들의 모임',
})
readonly description: string;

@ApiProperty({
description: '모임장 사용자의 ObjectId',
example: '60f4b3b3b3b3b3b3b3b3b3b3',
})
readonly manager: string;

@ApiProperty({
description: '부모임장 사용자의 ObjectId와 권한',
example: [
{
user: '60f4b3b3b3b3b3b3b3b3b3',
authorities: ['subManager'],
},
],
})
readonly subManagers: [
{
user: string;
authorities: string[];
},
];

@ApiProperty({
description: '모임원 사용자의 ObjectId',
example: ['60f4b3b3b3b3b3b3b3b3b3'],
})
readonly members: string[];
}
17 changes: 14 additions & 3 deletions src/group/dto/update-group.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import { PartialType } from '@nestjs/swagger';
import { CreateGroupDto } from './create-group.dto';
import { ApiProperty } from '@nestjs/swagger';

export class UpdateGroupDto extends PartialType(CreateGroupDto) {}
export class UpdateGroupDto {
@ApiProperty({
description: '모임 이름',
example: 'SSU',
})
readonly name: string;

@ApiProperty({
description: '모임 설명',
example: '숭실대학교 학생들의 모임',
})
readonly description: string;
}
14 changes: 13 additions & 1 deletion src/group/entities/group.entity.ts
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
export class Group {}
export class Group {
id: string;
name: string;
description: string;
manager: string;
subManagers: [
{
user: string;
authorities: string[];
},
];
members: string[];
}
37 changes: 33 additions & 4 deletions src/group/group.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,64 @@ import {
import { GroupService } from './group.service';
import { CreateGroupDto } from './dto/create-group.dto';
import { UpdateGroupDto } from './dto/update-group.dto';
import { ApiTags } from '@nestjs/swagger';
import { ApiOperation, ApiTags } from '@nestjs/swagger';

@ApiTags('Group')
@Controller('group')
export class GroupController {
constructor(private readonly groupService: GroupService) {}

@Post()
@ApiOperation({
summary: '모임 생성',
description: '모임을 생성합니다.',
})
create(@Body() createGroupDto: CreateGroupDto) {
return this.groupService.create(createGroupDto);
}

@Get()
@ApiOperation({
summary: '모든 모임 조회',
description: '모든 모임을 조회합니다.',
})
findAll() {
return this.groupService.findAll();
}

@Get(':id')
@ApiOperation({
summary: '모임 상세 조회',
description: '특정 모임을 조회합니다.',
})
findOne(@Param('id') id: string) {
return this.groupService.findOne(+id);
return this.groupService.findOne(id);
}

@Patch(':id')
@ApiOperation({
summary: '모임 수정',
description: '특정 모임을 수정합니다.',
})
update(@Param('id') id: string, @Body() updateGroupDto: UpdateGroupDto) {
return this.groupService.update(+id, updateGroupDto);
return this.groupService.update(id, updateGroupDto);
}

@Delete()
@ApiOperation({
summary: '모든 모임 삭제',
description: '모든 모임을 삭제합니다.',
})
removeAll() {
return this.groupService.removeAll();
}

@Delete(':id')
@ApiOperation({
summary: '모임 삭제',
description: '특정 모임을 삭제합니다.',
})
remove(@Param('id') id: string) {
return this.groupService.remove(+id);
return this.groupService.remove(id);
}
}
25 changes: 18 additions & 7 deletions src/group/group.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,33 @@ export class GroupService {
) {}

create(createGroupDto: CreateGroupDto) {
return this.groupModel.create(createGroupDto);
return new this.groupModel(createGroupDto).save();
}

async findAll(): Promise<Group[]> {
return this.groupModel.find().exec();
}

findOne(id: number) {
return `This action returns a #${id} group`;
removeAll() {
return this.groupModel.deleteMany({});
}

update(id: number, updateGroupDto: UpdateGroupDto) {
return { id, updateGroupDto };
async findOne(id: string): Promise<Group> {
return this.groupModel.findById(id).exec();
}

remove(id: number) {
return `This action removes a #${id} group`;
async update(id: string, updateGroupDto: UpdateGroupDto): Promise<Group> {
try {
const group = await this.groupModel.findById(id).exec();
group.name = updateGroupDto.name;
group.description = updateGroupDto.description;
return group.save();
} catch (e) {
return null;
}
}

async remove(id: string): Promise<Group> {
return this.groupModel.findByIdAndDelete(id);
}
}
11 changes: 10 additions & 1 deletion src/group/interfaces/group.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { Document } from 'mongoose';

export interface Group extends Document {
readonly name: string;
name: string;
description: string;
manager: string;
subManagers: [
{
user: string;
authorities: string[];
},
];
members: string[];
}
9 changes: 9 additions & 0 deletions src/group/schemas/group.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@ import * as mongoose from 'mongoose';

export const GroupSchema = new mongoose.Schema({
name: String,
description: String,
manager: String,
subManagers: [
{
user: String,
authorities: [String],
},
],
members: [String],
});
16 changes: 15 additions & 1 deletion src/member/dto/create-member.dto.ts
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
export class CreateMemberDto {}
import { ApiProperty } from '@nestjs/swagger';

export class CreateMemberDto {
@ApiProperty({
description: '회원 이름',
example: '박하은',
})
readonly name: string;

@ApiProperty({
description: '회원 연락처',
example: '010-1234-5678',
})
readonly phoneNumber: string;
}
16 changes: 14 additions & 2 deletions src/member/dto/update-member.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { PartialType } from '@nestjs/swagger';
import { ApiProperty, PartialType } from '@nestjs/swagger';
import { CreateMemberDto } from './create-member.dto';

export class UpdateMemberDto extends PartialType(CreateMemberDto) {}
export class UpdateMemberDto extends PartialType(CreateMemberDto) {
@ApiProperty({
description: '회원 이름',
example: '박하은',
})
readonly name: string;

@ApiProperty({
description: '회원 연락처',
example: '010-1234-5678',
})
readonly phoneNumber: string;
}
6 changes: 5 additions & 1 deletion src/member/entities/member.entity.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export class Member {}
export class Member {
id: string;
name: string;
phoneNumber: string;
}
6 changes: 6 additions & 0 deletions src/member/interfaces/member.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Document } from 'mongoose';

export interface Member extends Document {
name: string;
phoneNumber: string;
}
27 changes: 17 additions & 10 deletions src/member/member.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { Test, TestingModule } from '@nestjs/testing';
import { MemberController } from './member.controller';
import { MemberService } from './member.service';
import { Member } from './interfaces/member.interface';
import { HydratedDocument, Model } from 'mongoose';

describe('MemberController', () => {
let controller: MemberController;
let memberController: MemberController;
let memberService: MemberService;
let memberModel: Model<Member>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [MemberController],
providers: [MemberService],
}).compile();

controller = module.get<MemberController>(MemberController);
memberService = new MemberService(memberModel);
memberController = new MemberController(memberService);
});

it('should be defined', () => {
expect(controller).toBeDefined();
describe('findAll', () => {
it('should return an array of members', async () => {
const result: HydratedDocument<any> = [
{ _id: 1, name: '테스트1' },
{ _id: 2, name: '테스트2' },
];
jest.spyOn(memberService, 'findAll').mockImplementation(() => result);

expect(await memberController.findAll()).toBe(result);
});
});
});
37 changes: 33 additions & 4 deletions src/member/member.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,64 @@ import {
import { MemberService } from './member.service';
import { CreateMemberDto } from './dto/create-member.dto';
import { UpdateMemberDto } from './dto/update-member.dto';
import { ApiTags } from '@nestjs/swagger';
import { ApiOperation, ApiTags } from '@nestjs/swagger';

@ApiTags('Member')
@Controller('member')
export class MemberController {
constructor(private readonly memberService: MemberService) {}

@Post()
@ApiOperation({
summary: '회원 생성',
description: '회원을 생성합니다.',
})
create(@Body() createMemberDto: CreateMemberDto) {
return this.memberService.create(createMemberDto);
}

@Get()
@ApiOperation({
summary: '모든 회원 조회',
description: '모든 회원을 조회합니다.',
})
findAll() {
return this.memberService.findAll();
}

@Get(':id')
@ApiOperation({
summary: '회원 상세 조회',
description: '특정 회원을 조회합니다.',
})
findOne(@Param('id') id: string) {
return this.memberService.findOne(+id);
return this.memberService.findOne(id);
}

@Patch(':id')
@ApiOperation({
summary: '회원 수정',
description: '특정 회원을 수정합니다.',
})
update(@Param('id') id: string, @Body() updateMemberDto: UpdateMemberDto) {
return this.memberService.update(+id, updateMemberDto);
return this.memberService.update(id, updateMemberDto);
}

@Delete()
@ApiOperation({
summary: '모든 회원 삭제',
description: '모든 회원을 삭제합니다.',
})
removeAll() {
return this.memberService.removeAll();
}

@Delete(':id')
@ApiOperation({
summary: '회원 삭제',
description: '특정 회원을 삭제합니다.',
})
remove(@Param('id') id: string) {
return this.memberService.remove(+id);
return this.memberService.remove(id);
}
}
5 changes: 4 additions & 1 deletion src/member/member.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Module } from '@nestjs/common';
import { MemberService } from './member.service';
import { MemberController } from './member.controller';
import { DatabaseModule } from '../database/database.module';
import { memberProvider } from './member.provider';

@Module({
imports: [DatabaseModule],
controllers: [MemberController],
providers: [MemberService],
providers: [MemberService, ...memberProvider],
})
export class MemberModule {}
11 changes: 11 additions & 0 deletions src/member/member.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Connection } from 'mongoose';
import { MemberSchema } from './schemas/member.schema';

export const memberProvider = [
{
provide: 'MEMBER_MODEL',
useFactory: (connection: Connection) =>
connection.model('Member', MemberSchema),
inject: ['MONGODB_CONNECTION'],
},
];
Loading

0 comments on commit dcb8d5d

Please sign in to comment.