Skip to content

Commit 2d47b8a

Browse files
authored
Merge pull request #8 from wafflestudio/department-api
department-api
2 parents 15ebf1a + 4c5227c commit 2d47b8a

10 files changed

+265
-41
lines changed

src/department/constants.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export const DEPARTMENTS = [
2+
{
3+
name: '컴퓨터공학부',
4+
tags: [
5+
'입학',
6+
'등록/복학/휴학/재입학',
7+
'졸업',
8+
'수업',
9+
'학사(학부)',
10+
'학사(대학원)',
11+
'제 2전공/전과',
12+
'교환학생/유학',
13+
'장학',
14+
'학생공지(교내)',
15+
'외부행사/프로그램',
16+
'인턴/취업 (공식 게시)',
17+
'기업게시판',
18+
'연합전공인공지능',
19+
'자료실',
20+
'미분류',
21+
],
22+
},
23+
{
24+
name: '기계공학부',
25+
tags: ['공통', '학부', '대학원', '장학', '홍보'],
26+
},
27+
];

src/department/department.controller.spec.ts

-18
This file was deleted.
+51-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,52 @@
1-
import { Controller } from '@nestjs/common';
1+
import {
2+
Body,
3+
Controller,
4+
Delete,
5+
Get,
6+
Param,
7+
Post,
8+
Req,
9+
UseGuards,
10+
} from '@nestjs/common';
11+
import { JwtAccessGuard } from '../auth/auth.guard';
12+
import { UserRequest } from '../types/custom-type';
13+
import { Department } from './department.entity';
14+
import { FollowDto } from './dto/follow.dto';
15+
import { DepartmentService } from './department.service';
216

3-
@Controller('department')
4-
export class DepartmentController {}
17+
@UseGuards(JwtAccessGuard)
18+
@Controller('departments')
19+
export class DepartmentController {
20+
constructor(private departmentService: DepartmentService) {}
21+
22+
@Get()
23+
getAllDepartments(@Req() req: UserRequest): Promise<Department[]> {
24+
return this.departmentService.getAllDepartments(req);
25+
}
26+
27+
@Get('/:id')
28+
getDepartment(
29+
@Req() req: UserRequest,
30+
@Param('id') id: number,
31+
): Promise<Department> {
32+
return this.departmentService.getDepartment(req, id);
33+
}
34+
35+
@Post('/:id/follow')
36+
createFollow(
37+
@Req() req: UserRequest,
38+
@Param('id') id: number,
39+
@Body() followData: FollowDto,
40+
): Promise<Department> {
41+
return this.departmentService.createFollow(req, id, followData);
42+
}
43+
44+
@Delete('/:id/follow')
45+
deleteFollow(
46+
@Req() req: UserRequest,
47+
@Param('id') id: number,
48+
@Body() followData: FollowDto,
49+
): Promise<Department> {
50+
return this.departmentService.deleteFollow(req, id, followData);
51+
}
52+
}

src/department/department.entity.ts

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from 'typeorm';
99
import { User } from '../user/user.entity';
1010
import { Notice } from '../notice/notice.entity';
11+
import { Expose, Transform } from 'class-transformer';
1112

1213
@Entity()
1314
export class Department extends BaseEntity {
@@ -20,8 +21,11 @@ export class Department extends BaseEntity {
2021
@OneToMany(() => Notice, (notice) => notice.department)
2122
notices: Notice[];
2223

24+
@Transform((tags) => tags.value.map((tag) => tag.name))
2325
@OneToMany(() => Tag, (tag) => tag.department)
2426
tags: Tag[];
27+
28+
follow?: string[];
2529
}
2630

2731
@Entity()

src/department/department.init.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { DEPARTMENTS } from './constants';
2+
import { Department, Tag } from './department.entity';
3+
4+
export const departmentInit = async (): Promise<void> => {
5+
for (const departmentData of DEPARTMENTS) {
6+
const { name, tags } = departmentData;
7+
8+
let department: Department = await Department.findOne({ name });
9+
if (!department) {
10+
department = Department.create({ name });
11+
await Department.save(department);
12+
}
13+
14+
for (const tagName of tags) {
15+
let tag: Tag = await Tag.findOne({ name: tagName, department });
16+
if (!tag) {
17+
tag = Tag.create({ name: tagName, department });
18+
await Tag.save(tag);
19+
}
20+
}
21+
}
22+
};

src/department/department.service.spec.ts

-18
This file was deleted.

src/department/department.service.ts

+145-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,147 @@
1-
import { Injectable } from '@nestjs/common';
1+
import {
2+
BadRequestException,
3+
HttpException,
4+
HttpStatus,
5+
Injectable,
6+
NotFoundException,
7+
Req,
8+
} from '@nestjs/common';
9+
import { PreFollow, UserRequest } from '../types/custom-type';
10+
import { Department, Tag, UserTag } from './department.entity';
11+
import { FollowDto } from './dto/follow.dto';
12+
import { User } from '../user/user.entity';
13+
import { In } from 'typeorm';
214

315
@Injectable()
4-
export class DepartmentService {}
16+
export class DepartmentService {
17+
async getAllDepartments(@Req() req: UserRequest): Promise<Department[]> {
18+
const departments: Department[] = await Department.find({
19+
relations: ['tags'],
20+
});
21+
22+
return departments
23+
? await Promise.all(
24+
departments.map(async (department) => {
25+
department.follow = await this.getFollow(department, req.user);
26+
return department;
27+
}),
28+
)
29+
: [];
30+
}
31+
32+
async getDepartment(
33+
@Req() req: UserRequest,
34+
id: number,
35+
): Promise<Department> {
36+
const department: Department = await Department.findOne(id, {
37+
relations: ['tags'],
38+
});
39+
if (!department) {
40+
throw new NotFoundException('There is no department with the given id');
41+
}
42+
43+
department.follow = await this.getFollow(department, req.user);
44+
return department;
45+
}
46+
47+
async getFollow(department: Department, user: User): Promise<string[]> {
48+
user = await User.findOne(user);
49+
const tags: Tag[] = await Tag.find({
50+
department,
51+
});
52+
const userTags: UserTag[] = await UserTag.find({
53+
where: {
54+
user,
55+
tag: In(tags.map((tag) => tag.id)),
56+
},
57+
relations: ['tag'],
58+
});
59+
60+
return userTags ? userTags.map((userTag) => userTag.tag.name) : [];
61+
}
62+
63+
async validateIdFollow(
64+
req: UserRequest,
65+
id: number,
66+
followData: FollowDto,
67+
): Promise<PreFollow> {
68+
const department: Department = await Department.findOne(id, {
69+
relations: ['tags'],
70+
});
71+
if (!department) {
72+
throw new NotFoundException('There is no department with the id');
73+
}
74+
75+
const tag: Tag = department.tags.find(
76+
(tag) => tag.name === followData.follow,
77+
);
78+
if (!tag) {
79+
throw new BadRequestException(
80+
`There is no tag with the given name: ${followData.follow}`,
81+
);
82+
}
83+
const user: User = await User.findOne(req.user);
84+
const userTag: UserTag = await UserTag.findOne({
85+
user,
86+
tag,
87+
});
88+
89+
return {
90+
department,
91+
tag,
92+
user,
93+
userTag,
94+
};
95+
}
96+
97+
async createFollow(
98+
@Req() req,
99+
id: number,
100+
followData: FollowDto,
101+
): Promise<Department> {
102+
const { department, tag, user, userTag } = await this.validateIdFollow(
103+
req,
104+
id,
105+
followData,
106+
);
107+
108+
if (userTag) {
109+
throw new BadRequestException('already followed tag');
110+
}
111+
112+
const newUserTag: UserTag = UserTag.create({
113+
user,
114+
tag,
115+
});
116+
await UserTag.save(newUserTag);
117+
118+
department.follow = await this.getFollow(department, user);
119+
return department;
120+
}
121+
122+
async deleteFollow(
123+
@Req() req,
124+
id: number,
125+
followData: FollowDto,
126+
): Promise<Department> {
127+
const { department, tag, user, userTag } = await this.validateIdFollow(
128+
req,
129+
id,
130+
followData,
131+
);
132+
133+
if (!userTag) {
134+
throw new HttpException(
135+
{
136+
message: 'already deleted follow',
137+
},
138+
HttpStatus.NO_CONTENT,
139+
);
140+
}
141+
142+
await UserTag.delete(userTag);
143+
144+
department.follow = await this.getFollow(department, user);
145+
return department;
146+
}
147+
}

src/department/dto/follow.dto.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { IsString } from 'class-validator';
2+
3+
export class FollowDto {
4+
@IsString()
5+
follow: string;
6+
}

src/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { NestFactory, Reflector } from '@nestjs/core';
22
import { AppModule } from './app.module';
33
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';
4+
import { departmentInit } from './department/department.init';
45

56
async function bootstrap() {
67
const app = await NestFactory.create(AppModule);
8+
await departmentInit();
79
app.useGlobalPipes(
810
new ValidationPipe({
911
whitelist: true,

src/types/custom-type.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Request } from 'express';
22
import { User } from '../user/user.entity';
3+
import { Department, Tag, UserTag } from '../department/department.entity';
34

45
export class Payload {
56
username: string;
@@ -9,3 +10,10 @@ export class Payload {
910
export interface UserRequest extends Request {
1011
user?: User;
1112
}
13+
14+
export interface PreFollow {
15+
department: Department;
16+
tag: Tag;
17+
user: User;
18+
userTag: UserTag;
19+
}

0 commit comments

Comments
 (0)