From d48ab41d1369b358c2fd564efc29c2677cac21d4 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 15:37:01 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20deleteDiary=20=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /diaries DELETE 정상 요청의 응답 코드를 204로 수정 - 함수명을 deleteBoad에서 deleteDiary로 변경 - deleteDiary에 async 추가 --- BE/src/diaries/diaries.controller.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BE/src/diaries/diaries.controller.ts b/BE/src/diaries/diaries.controller.ts index a1d37bd..492d034 100644 --- a/BE/src/diaries/diaries.controller.ts +++ b/BE/src/diaries/diaries.controller.ts @@ -112,8 +112,10 @@ export class DiariesController { @Delete("/:uuid") @UseGuards(IdGuard) - deleteBoard(@Param("uuid") uuid: string): Promise { + @HttpCode(204) + async deleteDiary(@Param("uuid") uuid: string): Promise { const deleteDiaryDto: DeleteDiaryDto = { uuid }; - return this.diariesService.deleteDiary(deleteDiaryDto); + await this.diariesService.deleteDiary(deleteDiaryDto); + return; } } From 3ffa1c045a6c39ed1939ff98b37201274f97e26b Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 15:39:09 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20IdGuard=EB=A5=BC=20PrivateDiaryGuar?= =?UTF-8?q?d=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 타인의 일기에 대한 접근을 막는 IdGuard를 PrivateDiaryGuard로 네이밍 수정 - 파일 명을 id-guard.ts에서 diary-guard.ts로 변경 - PrivateDiaryGuard의 NotFoundExceptino에 메세지 추가 --- BE/src/auth/auth.module.ts | 4 ++-- .../auth/guard/{auth.id-guard.ts => auth.diary-guard.ts} | 4 ++-- BE/src/diaries/diaries.controller.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) rename BE/src/auth/guard/{auth.id-guard.ts => auth.diary-guard.ts} (88%) diff --git a/BE/src/auth/auth.module.ts b/BE/src/auth/auth.module.ts index 17733c0..c787c24 100644 --- a/BE/src/auth/auth.module.ts +++ b/BE/src/auth/auth.module.ts @@ -6,7 +6,7 @@ import { JwtModule } from "@nestjs/jwt"; import { PassportModule } from "@nestjs/passport"; import { JwtStrategy } from "./jwt.strategy"; import { UsersModule } from "src/users/users.module"; -import { IdGuard } from "./guard/auth.id-guard"; +import { PrivateDiaryGuard } from "./guard/auth.diary-guard"; @Module({ imports: [ @@ -20,7 +20,7 @@ import { IdGuard } from "./guard/auth.id-guard"; UsersModule, ], controllers: [AuthController], - providers: [AuthService, JwtStrategy, IdGuard], + providers: [AuthService, JwtStrategy, PrivateDiaryGuard], exports: [PassportModule], }) export class AuthModule {} diff --git a/BE/src/auth/guard/auth.id-guard.ts b/BE/src/auth/guard/auth.diary-guard.ts similarity index 88% rename from BE/src/auth/guard/auth.id-guard.ts rename to BE/src/auth/guard/auth.diary-guard.ts index 19291ab..155fd69 100644 --- a/BE/src/auth/guard/auth.id-guard.ts +++ b/BE/src/auth/guard/auth.diary-guard.ts @@ -7,7 +7,7 @@ import { AuthGuard } from "@nestjs/passport"; import { DiariesRepository } from "src/diaries/diaries.repository"; @Injectable() -export class IdGuard extends AuthGuard("jwt") { +export class PrivateDiaryGuard extends AuthGuard("jwt") { constructor(private readonly diariesRepository: DiariesRepository) { super(); } @@ -30,7 +30,7 @@ export class IdGuard extends AuthGuard("jwt") { if (this.getUserId(request.user) === requestDiary.user.userId) { return true; } else { - throw new NotFoundException(); + throw new NotFoundException("존재하지 않는 일기입니다."); } } diff --git a/BE/src/diaries/diaries.controller.ts b/BE/src/diaries/diaries.controller.ts index 492d034..b15f5f7 100644 --- a/BE/src/diaries/diaries.controller.ts +++ b/BE/src/diaries/diaries.controller.ts @@ -20,7 +20,7 @@ import { } from "./diaries.dto"; import { Diary } from "./diaries.entity"; import { AuthGuard } from "@nestjs/passport"; -import { IdGuard } from "src/auth/guard/auth.id-guard"; +import { PrivateDiaryGuard } from "src/auth/guard/auth.diary-guard"; import { GetUser } from "src/auth/get-user.decorator"; import { User } from "src/users/users.entity"; @@ -39,7 +39,7 @@ export class DiariesController { } @Get("/:uuid") - @UseGuards(IdGuard) + @UseGuards(PrivateDiaryGuard) async readDiary(@Param("uuid") uuid: string): Promise { const readDiaryDto: ReadDiaryDto = { uuid }; const diary = await this.diariesService.readDiary(readDiaryDto); @@ -105,13 +105,13 @@ export class DiariesController { } @Put() - @UseGuards(IdGuard) + @UseGuards(PrivateDiaryGuard) modifyDiary(@Body() updateDiaryDto: UpdateDiaryDto): Promise { return this.diariesService.modifyDiary(updateDiaryDto); } @Delete("/:uuid") - @UseGuards(IdGuard) + @UseGuards(PrivateDiaryGuard) @HttpCode(204) async deleteDiary(@Param("uuid") uuid: string): Promise { const deleteDiaryDto: DeleteDiaryDto = { uuid }; From 6eaf266586fa5b8bcde37f6942fa541bb6513f52 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 15:39:55 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EC=9D=BC=EA=B8=B0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=9A=94=EC=B2=AD=EC=97=90=20=EB=B6=80=EC=A0=81?= =?UTF-8?q?=EC=A0=88=ED=95=9C=20uuid=20=ED=8F=AC=ED=95=A8=20=EC=8B=9C=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=9D=91=EB=8B=B5=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 부적절한 UUID 양식인지 검사하는 IsUUID에 오류 메세지 추가 --- BE/src/diaries/diaries.dto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/diaries/diaries.dto.ts b/BE/src/diaries/diaries.dto.ts index 61ab18d..bafea3a 100644 --- a/BE/src/diaries/diaries.dto.ts +++ b/BE/src/diaries/diaries.dto.ts @@ -68,7 +68,7 @@ export class UpdateDiaryDto { } export class DeleteDiaryDto { - @IsUUID() + @IsUUID("4", { message: "일기 uuid 값이 uuid 양식이어야 합니다." }) uuid: string; } From efc31012d7a299d20438a33b592bb250f29c9e97 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 15:42:48 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=BC=EA=B8=B0=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=82=AD=EC=A0=9C=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=9D=91=EB=8B=B5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 존재하지 않는 일기에 대한 삭제 요청 시 404 Not Found를 응답 --- BE/src/diaries/diaries.repository.ts | 9 ++++----- BE/src/diaries/diaries.service.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/BE/src/diaries/diaries.repository.ts b/BE/src/diaries/diaries.repository.ts index 290fe7c..533f7dc 100644 --- a/BE/src/diaries/diaries.repository.ts +++ b/BE/src/diaries/diaries.repository.ts @@ -82,15 +82,14 @@ export class DiariesRepository { async deleteDiary(deleteDiaryDto: DeleteDiaryDto): Promise { const { uuid } = deleteDiaryDto; const diary = await this.getDiaryByUuid(uuid); + if (!diary) { + throw new Error(); + } await Diary.softRemove(diary); } async getDiaryByUuid(uuid: string): Promise { - const found = await Diary.findOneBy({ uuid }); - if (!found) { - throw new NotFoundException(`Can't find Diary with uuid: [${uuid}]`); - } - return found; + return Diary.findOneBy({ uuid }); } } diff --git a/BE/src/diaries/diaries.service.ts b/BE/src/diaries/diaries.service.ts index 0b2dfa5..620721d 100644 --- a/BE/src/diaries/diaries.service.ts +++ b/BE/src/diaries/diaries.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { DiariesRepository } from "./diaries.repository"; import { Diary } from "./diaries.entity"; import { @@ -68,6 +68,11 @@ export class DiariesService { } async deleteDiary(deleteDiaryDto: DeleteDiaryDto): Promise { - return this.diariesRepository.deleteDiary(deleteDiaryDto); + try { + await this.diariesRepository.deleteDiary(deleteDiaryDto); + } catch { + throw new NotFoundException("존재하지 않는 일기입니다."); + } + return; } } From ab9f42cc7ea1fad3cc180b4fa569e446bb55067e Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 15:59:08 +0900 Subject: [PATCH 5/8] =?UTF-8?q?test:=20=EC=9D=BC=EA=B8=B0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=A0=95=EC=83=81=20=EC=9A=94=EC=B2=AD=20e2e=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 삭제에 대한 정상 요청 시 204 No Content를 응답하는 e2e 테스트 작성 --- BE/test/diaries.delete.e2e-spec.ts | 58 ++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 BE/test/diaries.delete.e2e-spec.ts diff --git a/BE/test/diaries.delete.e2e-spec.ts b/BE/test/diaries.delete.e2e-spec.ts new file mode 100644 index 0000000..20f61ee --- /dev/null +++ b/BE/test/diaries.delete.e2e-spec.ts @@ -0,0 +1,58 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { INestApplication } from "@nestjs/common"; +import * as request from "supertest"; +import { AppModule } from "../src/app.module"; +import { ValidationPipe } from "@nestjs/common"; + +describe("AppController (e2e)", () => { + let app: INestApplication; + let accessToken: string; + let diaryUuid: string; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + app.enableCors(); + app.useGlobalPipes(new ValidationPipe()); + + await app.init(); + + const signInPost = await request(app.getHttpServer()) + .post("/auth/signin") + .send({ + userId: "commonUser", + password: process.env.COMMON_USER_PASS, + }); + + accessToken = signInPost.body.accessToken; + + const createDiaryPost = await request(app.getHttpServer()) + .post("/diaries") + .set("Authorization", `Bearer ${accessToken}`) + .send({ + title: "title", + content: "this is content.", + point: "1.5,5.5,10.55", + date: "2023-11-14", + tags: ["tagTest", "tagTest2"], + shapeUuid: "0c99bbc6-e404-464b-a310-5bf0fa0f0fa7", + }); + + diaryUuid = createDiaryPost.body.uuid; + console.log(diaryUuid); + }); + + afterEach(async () => { + await app.close(); + }); + + it("정상 요청 시 200 OK 응답", async () => { + const postResponse = await request(app.getHttpServer()) + .delete(`/diaries/${diaryUuid}`) + .set("Authorization", `Bearer ${accessToken}`) + .expect(204); + }); +}); From 06f5b158534f7af53b31de89aaec3b6795b481ca Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 16:04:32 +0900 Subject: [PATCH 6/8] =?UTF-8?q?test:=20=EC=9D=BC=EA=B8=B0=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8B=9C=20=EC=95=A1=EC=84=B8=EC=8A=A4=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=97=86=EB=8A=94=20=EC=9A=94=EC=B2=AD=20e2e=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 삭제에 대해 액세스 토큰 없이 요청 시 401 Unauthorized를 응답하는 e2e 테스트 작성 --- BE/test/diaries.delete.e2e-spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/BE/test/diaries.delete.e2e-spec.ts b/BE/test/diaries.delete.e2e-spec.ts index 20f61ee..10213ba 100644 --- a/BE/test/diaries.delete.e2e-spec.ts +++ b/BE/test/diaries.delete.e2e-spec.ts @@ -55,4 +55,14 @@ describe("AppController (e2e)", () => { .set("Authorization", `Bearer ${accessToken}`) .expect(204); }); + + it("액세스 토큰 없이 요청 시 401 Unauthorized 응답", async () => { + const postResponse = await request(app.getHttpServer()) + .delete(`/diaries/${diaryUuid}`) + .expect(401); + + const body = JSON.parse(postResponse.text); + + expect(body.message).toBe("Unauthorized"); + }); }); From 351867c0ecb030036f8b88fccc9e3ef85a729b36 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 16:05:26 +0900 Subject: [PATCH 7/8] =?UTF-8?q?test:=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=BC=EA=B8=B0=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=9A=94=EC=B2=AD=20e2e=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 존재하지 않는 일기에 대한 요청 시 404 Not Found를 응답하는 e2e 테스트 작성 - 타인의 일기에 대한 요청 시 404 Not Found를 응답하는 e2e 테스트 주석으로 작성 - 추후 회원가입 응답 구현 완료 시 구현 예정 --- BE/test/diaries.delete.e2e-spec.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/BE/test/diaries.delete.e2e-spec.ts b/BE/test/diaries.delete.e2e-spec.ts index 10213ba..5ae98a2 100644 --- a/BE/test/diaries.delete.e2e-spec.ts +++ b/BE/test/diaries.delete.e2e-spec.ts @@ -65,4 +65,27 @@ describe("AppController (e2e)", () => { expect(body.message).toBe("Unauthorized"); }); + + it("존재하지 않는 일기에 대한 요청 시 404 Not Found 응답", async () => { + const postResponse = await request(app.getHttpServer()) + .delete(`/diaries/5d4a854d-cd2d-46a8-8adc-acec0270e4dc`) + .set("Authorization", `Bearer ${accessToken}`) + .expect(404); + + const body = JSON.parse(postResponse.text); + + expect(body.message).toContain("존재하지 않는 일기입니다."); + }); + + // 유저 회원가입 및 로그인 후 글 생성하고 commonUser에서 해당 글에 대해 삭제 요청 보내기 + // it("타인의 일기에 대한 요청 시 404 Not Found 응답", async () => { + // const postResponse = await request(app.getHttpServer()) + // .delete(`/diaries/${diaryUuid}`) + // .set("Authorization", `Bearer ${accessToken}`) + // .expect(204); + + // const body = JSON.parse(postResponse.text); + + // expect(body.message).toContain("존재하지 않는 일기입니다."); + // }); }); From e6a9164a7963ba5e908cbce81e64d4ed34a0351b Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Wed, 22 Nov 2023 16:16:13 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor:=20getDiaryByUuid=20=EC=9B=90?= =?UTF-8?q?=EC=83=81=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getDiaryByUuid에서 직접 에러를 throw하도록 원상복구 --- BE/src/diaries/diaries.repository.ts | 9 +++++---- BE/src/diaries/diaries.service.ts | 6 +----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/BE/src/diaries/diaries.repository.ts b/BE/src/diaries/diaries.repository.ts index 533f7dc..d8fcfc5 100644 --- a/BE/src/diaries/diaries.repository.ts +++ b/BE/src/diaries/diaries.repository.ts @@ -82,14 +82,15 @@ export class DiariesRepository { async deleteDiary(deleteDiaryDto: DeleteDiaryDto): Promise { const { uuid } = deleteDiaryDto; const diary = await this.getDiaryByUuid(uuid); - if (!diary) { - throw new Error(); - } await Diary.softRemove(diary); } async getDiaryByUuid(uuid: string): Promise { - return Diary.findOneBy({ uuid }); + const found = await Diary.findOneBy({ uuid }); + if (!found) { + throw new NotFoundException(`존재하지 않는 일기입니다.`); + } + return found; } } diff --git a/BE/src/diaries/diaries.service.ts b/BE/src/diaries/diaries.service.ts index 620721d..c42b373 100644 --- a/BE/src/diaries/diaries.service.ts +++ b/BE/src/diaries/diaries.service.ts @@ -68,11 +68,7 @@ export class DiariesService { } async deleteDiary(deleteDiaryDto: DeleteDiaryDto): Promise { - try { - await this.diariesRepository.deleteDiary(deleteDiaryDto); - } catch { - throw new NotFoundException("존재하지 않는 일기입니다."); - } + await this.diariesRepository.deleteDiary(deleteDiaryDto); return; } }