Skip to content

Commit

Permalink
Merge pull request #111 from boostcampwm2023/feat/103-diary-update-re…
Browse files Browse the repository at this point in the history
…sponse-handling

[Feat] 일기 수정 API에 대한 서버 응답 수정 및 e2e 테스트 작성
  • Loading branch information
mingxoxo authored Nov 23, 2023
2 parents 057dbb7 + f87e0b3 commit 0d824a0
Show file tree
Hide file tree
Showing 14 changed files with 446 additions and 51 deletions.
4 changes: 2 additions & 2 deletions BE/src/auth/guard/auth.diary-guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
Injectable,
NotFoundException,
} from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { DiariesRepository } from "src/diaries/diaries.repository";
import { JwtAuthGuard } from "./auth.jwt-guard";

@Injectable()
export class PrivateDiaryGuard extends AuthGuard("jwt") {
export class PrivateDiaryGuard extends JwtAuthGuard {
constructor(private readonly diariesRepository: DiariesRepository) {
super();
}
Expand Down
19 changes: 19 additions & 0 deletions BE/src/auth/guard/auth.jwt-guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { AuthGuard as NestAuthGuard } from "@nestjs/passport";

@Injectable()
export class JwtAuthGuard extends NestAuthGuard("jwt") {
handleRequest(err, user, info: Error) {
if (err || !user) {
if (info.message === "No auth token") {
throw new UnauthorizedException("비로그인 상태의 요청입니다.");
} else if (info.message === "jwt expired") {
throw new UnauthorizedException("토큰이 만료되었습니다.");
} else if (info.message === "invalid token") {
throw new UnauthorizedException("유효하지 않은 토큰입니다.");
}
throw err || new UnauthorizedException("Unauthorized");
}
return user;
}
}
17 changes: 16 additions & 1 deletion BE/src/auth/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
});
}

async validate(payload) {
async validate(payload: any): Promise<User> {
if (!payload) {
throw new UnauthorizedException();
}

if (!this.isValidPayload(payload)) {
throw new UnauthorizedException();
}

const { userId } = payload;
const user: User = await this.userRepository.getUserByUserId(userId);

Expand All @@ -22,4 +30,11 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
}
return user;
}

isValidPayload(payload: any) {
if (typeof payload !== "object") return false;
if (!payload.hasOwnProperty("userId")) return false;

return true;
}
}
19 changes: 14 additions & 5 deletions BE/src/diaries/diaries.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,28 @@ import {
UpdateDiaryDto,
} from "./dto/diaries.dto";
import { Diary } from "./diaries.entity";
import { AuthGuard } from "@nestjs/passport";
import { ReadDiaryDto, ReadDiaryResponseDto } from "./dto/diaries.read.dto";
import { PrivateDiaryGuard } from "src/auth/guard/auth.diary-guard";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/users/users.entity";
import { Tag } from "src/tags/tags.entity";
import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard";

@Controller("diaries")
@UseGuards(AuthGuard())
@UseGuards(JwtAuthGuard)
export class DiariesController {
constructor(private diariesService: DiariesService) {}

@Post()
@HttpCode(201)
async writeDiary(
@Body() createDiaryDto: CreateDiaryDto,
@GetUser() user: User,
): Promise<DiaryUuidDto> {
const diary: Diary = await this.diariesService.writeDiary(createDiaryDto);
const diary: Diary = await this.diariesService.writeDiary(
createDiaryDto,
user,
);
return { uuid: diary.uuid };
}

Expand All @@ -61,8 +65,13 @@ export class DiariesController {

@Put()
@UseGuards(PrivateDiaryGuard)
modifyDiary(@Body() updateDiaryDto: UpdateDiaryDto): Promise<Diary> {
return this.diariesService.modifyDiary(updateDiaryDto);
@HttpCode(204)
async modifyDiary(
@Body() updateDiaryDto: UpdateDiaryDto,
@GetUser() user: User,
): Promise<void> {
await this.diariesService.modifyDiary(updateDiaryDto, user);
return;
}

@Delete("/:uuid")
Expand Down
38 changes: 29 additions & 9 deletions BE/src/diaries/diaries.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ export class DiariesRepository {
createDiaryDto: CreateDiaryDto,
encodedContent: string,
tags: Tag[],
user: User,
): Promise<Diary> {
const { title, point, date } = createDiaryDto;
const { title, point, date, shapeUuid } = createDiaryDto;
const content = encodedContent;
const shape = await Shape.findOne({ where: { uuid: shapeUuid } });

// 미구현 기능을 대체하기 위한 임시 값
const positiveRatio = 0.0;
const negativeRatio = 100.0;
const neutralRatio = 0.0;
const sentiment = sentimentStatus.NEUTRAL;
const shape = await Shape.findOne({ where: { id: 1 } });
const user = await User.findOne({ where: { id: 1 } });

const newDiary = Diary.create({
title,
Expand Down Expand Up @@ -61,15 +61,35 @@ export class DiariesRepository {

async updateDiary(
updateDiaryDto: UpdateDiaryDto,
encodedContent: string,
encryptedContent: string,
tags: Tag[],
user: User,
): Promise<Diary> {
const { uuid, title, date, shapeUuid } = updateDiaryDto;
const { uuid, title, point, date, shapeUuid } = updateDiaryDto;
const content = encryptedContent;
const shape = await Shape.findOne({ where: { uuid: shapeUuid } });

// 미구현 기능을 대체하기 위한 임시 값
const positiveRatio = 0.0;
const negativeRatio = 100.0;
const neutralRatio = 0.0;
const sentiment = sentimentStatus.NEUTRAL;

const diary = await this.getDiaryByUuid(uuid);

diary.title = title;
diary.date = date;
diary.content = encodedContent;
diary.shape = await Shape.findOne({ where: { uuid: shapeUuid } });
Object.assign(diary, {
title,
content,
point,
date,
positiveRatio,
negativeRatio,
neutralRatio,
sentiment,
shape,
user,
tags,
});

await diary.save();
return diary;
Expand Down
29 changes: 26 additions & 3 deletions BE/src/diaries/diaries.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { TagsRepository } from "src/tags/tags.repository";
import { Tag } from "src/tags/tags.entity";
import { ReadDiaryDto } from "./dto/diaries.read.dto";
import { User } from "src/users/users.entity";
import { createCipheriv, createDecipheriv } from "crypto";

@Injectable()
Expand All @@ -18,7 +19,7 @@ export class DiariesService {
private tagsRepository: TagsRepository,
) {}

async writeDiary(createDiaryDto: CreateDiaryDto): Promise<Diary> {
async writeDiary(createDiaryDto: CreateDiaryDto, user: User): Promise<Diary> {
const tags = [];

const cipher = createCipheriv(
Expand All @@ -44,6 +45,7 @@ export class DiariesService {
createDiaryDto,
encryptedContent,
tags,
user,
);

return diary;
Expand Down Expand Up @@ -89,7 +91,23 @@ export class DiariesService {
return diaryList;
}

async modifyDiary(updateDiaryDto: UpdateDiaryDto): Promise<Diary> {
async modifyDiary(
updateDiaryDto: UpdateDiaryDto,
user: User,
): Promise<Diary> {
const tags = [];

await Promise.all(
updateDiaryDto.tags.map(async (tag) => {
if ((await Tag.findOne({ where: { name: tag } })) !== null) {
const tagEntity = await Tag.findOneBy({ name: tag });
tags.push(tagEntity);
} else {
tags.push(await this.tagsRepository.createTag(tag));
}
}),
);

const cipher = createCipheriv(
"aes-256-cbc",
process.env.CONTENT_SECRET_KEY,
Expand All @@ -98,7 +116,12 @@ export class DiariesService {
let encryptedContent = cipher.update(updateDiaryDto.content, "utf8", "hex");
encryptedContent += cipher.final("hex");

return this.diariesRepository.updateDiary(updateDiaryDto, encryptedContent);
return this.diariesRepository.updateDiary(
updateDiaryDto,
encryptedContent,
tags,
user,
);
}

async deleteDiary(deleteDiaryDto: DeleteDiaryDto): Promise<void> {
Expand Down
28 changes: 4 additions & 24 deletions BE/src/diaries/dto/diaries.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
IsString,
IsDate,
Matches,
IsUUID,
IsArray,
Expand Down Expand Up @@ -35,34 +34,15 @@ export class CreateDiaryDto {
shapeUuid: string;
}

export class UpdateDiaryDto {
@IsUUID()
export class UpdateDiaryDto extends CreateDiaryDto {
@IsUUID("4", { message: "일기 uuid 값이 uuid 양식이어야 합니다." })
@IsNotEmpty({ message: "일기 uuid는 비어있지 않아야 합니다." })
uuid: string;

@IsString()
title: string;

@IsString()
content: string;

@IsString()
@Matches(/^-?\d+(\.\d+)?,-?\d+(\.\d+)?,-?\d+(\.\d+)?$/, {
message: "적절하지 않은 포인트 양식입니다",
})
point: string;

@IsDate()
date: Date;

@IsArray()
tags: string[];

@IsUUID()
shapeUuid: string;
}

export class DeleteDiaryDto {
@IsUUID("4", { message: "일기 uuid 값이 uuid 양식이어야 합니다." })
@IsNotEmpty({ message: "일기 uuid는 비어있지 않아야 합니다." })
uuid: string;
}

Expand Down
4 changes: 2 additions & 2 deletions BE/src/shapes/shapes.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
UseGuards,
} from "@nestjs/common";
import { ShapesService } from "./shapes.service";
import { AuthGuard } from "@nestjs/passport";
import { Shape } from "./shapes.entity";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/users/users.entity";
import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard";

@Controller("shapes")
export class ShapesController {
Expand All @@ -23,7 +23,7 @@ export class ShapesController {
}

@Get("/:uuid")
@UseGuards(AuthGuard())
@UseGuards(JwtAuthGuard)
@Header("Content-Type", "image/png")
async getShapeFilesByUuid(
@Param("uuid") uuid: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { AppModule } from "../../src/app.module";
import { ValidationPipe } from "@nestjs/common";

describe("/auth/signin (e2e)", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { AppModule } from "../../src/app.module";
import { ValidationPipe } from "@nestjs/common";

describe("AppController (e2e)", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { AppModule } from "../../src/app.module";
import { ValidationPipe } from "@nestjs/common";

describe("AppController (e2e)", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { AppModule } from "../../src/app.module";
import { ValidationPipe } from "@nestjs/common";

describe("AppController (e2e)", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "../src/app.module";
import { AppModule } from "../../src/app.module";
import { ValidationPipe } from "@nestjs/common";
import { UsersRepository } from "src/users/users.repository";

Expand Down
Loading

0 comments on commit 0d824a0

Please sign in to comment.