From dade666552a429b9aa55aaf56141f77ee87b988b Mon Sep 17 00:00:00 2001 From: suno-boy Date: Sat, 12 Oct 2024 14:44:06 +0900 Subject: [PATCH 01/63] test --- .../team1/BE/seamless/controller/ProjectInviteController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java b/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java index 8a3b139..d392ca7 100644 --- a/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java +++ b/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java @@ -18,6 +18,7 @@ @RequestMapping("/api/project") public class ProjectInviteController { + @Autowired private ProjectInviteService inviteService; From b746d8c6ec8ea657c798da880b7f421a45e3dfb8 Mon Sep 17 00:00:00 2001 From: suno-boy Date: Sat, 12 Oct 2024 14:45:26 +0900 Subject: [PATCH 02/63] test --- .../team1/BE/seamless/controller/ProjectInviteController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java b/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java index d392ca7..8a3b139 100644 --- a/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java +++ b/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java @@ -18,7 +18,6 @@ @RequestMapping("/api/project") public class ProjectInviteController { - @Autowired private ProjectInviteService inviteService; From 039c73e38d3c08736408f77388f2a5483bc93b4d Mon Sep 17 00:00:00 2001 From: suno-boy Date: Sat, 12 Oct 2024 14:52:26 +0900 Subject: [PATCH 03/63] =?UTF-8?q?style:=20=EC=9D=B4=EB=A9=94=EC=9D=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B0=B8=EC=97=AC=EC=BD=94=EB=93=9C=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=93=A4=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=9D=BC=EA=B4=80=EC=A0=81=EC=9D=B4?= =?UTF-8?q?=EA=B3=A0,=20=EC=A7=81=EA=B4=80=EC=A0=81=EC=9D=B4=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...viteController.java => InviteCodeByEmailController.java} | 6 +++--- ...jectInviteService.java => InviteCodeByEmailService.java} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/team1/BE/seamless/controller/{ProjectInviteController.java => InviteCodeByEmailController.java} (90%) rename src/main/java/team1/BE/seamless/service/{ProjectInviteService.java => InviteCodeByEmailService.java} (95%) diff --git a/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java b/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java similarity index 90% rename from src/main/java/team1/BE/seamless/controller/ProjectInviteController.java rename to src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java index 8a3b139..e7f1f14 100644 --- a/src/main/java/team1/BE/seamless/controller/ProjectInviteController.java +++ b/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java @@ -10,16 +10,16 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.InviteRequestDTO; -import team1.BE.seamless.service.ProjectInviteService; +import team1.BE.seamless.service.InviteCodeByEmailService; import team1.BE.seamless.util.page.SingleResult; @Tag(name = "이메일 전송") @RestController @RequestMapping("/api/project") -public class ProjectInviteController { +public class InviteCodeByEmailController { @Autowired - private ProjectInviteService inviteService; + private InviteCodeByEmailService inviteService; @Operation(summary = "이메일로 참여코드 전송하기") @PostMapping("/invite") diff --git a/src/main/java/team1/BE/seamless/service/ProjectInviteService.java b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java similarity index 95% rename from src/main/java/team1/BE/seamless/service/ProjectInviteService.java rename to src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java index 816b8c4..7c8df40 100644 --- a/src/main/java/team1/BE/seamless/service/ProjectInviteService.java +++ b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Service; @Service -public class ProjectInviteService { +public class InviteCodeByEmailService { @Autowired private JavaMailSender mailSender; From d56e9f45db8846de5e5b2d68f97794193a6cf6bd Mon Sep 17 00:00:00 2001 From: suno-boy Date: Sat, 12 Oct 2024 16:19:13 +0900 Subject: [PATCH 04/63] =?UTF-8?q?fix:=20=EC=B4=88=EB=8C=80=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9A=94=EC=B2=AD=20DTO=EC=97=90=EC=84=9C=20projec?= =?UTF-8?q?tId=20=ED=83=80=EC=9E=85=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java b/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java index 3d0a775..4022a67 100644 --- a/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java @@ -4,22 +4,22 @@ // 요청을 보낼 때의 DTO public class InviteRequestDTO { - private Integer projectId; + private Long projectId; private String email; public InviteRequestDTO() { } - public InviteRequestDTO(Integer projectId, String email) { + public InviteRequestDTO(Long projectId, String email) { this.projectId = projectId; this.email = email; } - public Integer getProjectId() { + public Long getProjectId() { return projectId; } - public void setProjectId(Integer projectId) { + public void setProjectId(Long projectId) { this.projectId = projectId; } From ea1be2a890456bab1639b62286905f6b6768effa Mon Sep 17 00:00:00 2001 From: suno-boy Date: Sat, 12 Oct 2024 16:19:57 +0900 Subject: [PATCH 05/63] =?UTF-8?q?fix:=20=EC=B4=88=EB=8C=80=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=97=90=EB=9F=AC=EC=B2=98=EB=A6=AC=20BaseHandler?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/InviteCodeByEmailController.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java b/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java index e7f1f14..c73c6da 100644 --- a/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java +++ b/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java @@ -5,12 +5,14 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.InviteRequestDTO; import team1.BE.seamless.service.InviteCodeByEmailService; +import team1.BE.seamless.util.errorException.BaseHandler; import team1.BE.seamless.util.page.SingleResult; @Tag(name = "이메일 전송") @@ -27,11 +29,11 @@ public SingleResult inviteMemberToProject(@RequestBody InviteRequestDTO try { String message = "You have been invited to join the project with ID: " + inviteRequest.getProjectId() - + "\nAnd Participation code: "; - inviteService.sendProjectInvite(inviteRequest.getEmail(), message); - return new SingleResult<>("프로젝트 초대 요청이 성공적으로 처리되었습니다."); + + "\nAnd Participation code: 참여코드 들어가야함"; + inviteService.sendProjectInvite(inviteRequest.getEmail(), message, inviteRequest.getProjectId()); + return new SingleResult<>("이메일로 프로젝트 초대코드 전송이 성공적으로 처리되었습니다."); } catch (Exception e) { - return new SingleResult<>("이메일 초대 실패: " + e.getMessage()); + throw new BaseHandler(HttpStatus.BAD_REQUEST,"이메일로 프로젝트 초대코드 전송이 실패되었습니다. : " + e.getMessage()); } } } \ No newline at end of file From f9ad255b406c0b57525c44686efeedefcb4374ae Mon Sep 17 00:00:00 2001 From: suno-boy Date: Sat, 12 Oct 2024 16:20:31 +0900 Subject: [PATCH 06/63] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EC=84=9C=EB=B9=84=EC=8A=A4=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=EC=97=90=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/InviteCodeByEmailService.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java index 7c8df40..0b5c5d1 100644 --- a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java +++ b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java @@ -3,20 +3,43 @@ // 요청을 보낼 때의 서비스 계층 import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Service; +import team1.BE.seamless.entity.ProjectEntity; +import team1.BE.seamless.repository.ProjectRepository; +import team1.BE.seamless.util.errorException.BaseHandler; + +import java.time.LocalDateTime; @Service public class InviteCodeByEmailService { - @Autowired private JavaMailSender mailSender; + private ProjectRepository projectRepository; + + @Autowired + InviteCodeByEmailService(JavaMailSender mailSender, ProjectRepository projectRepository) { + this.mailSender = mailSender; + this.projectRepository = projectRepository; + } + + public void sendProjectInvite(String email, String message, Long projectId) { + // 프로젝트 존재 검증 + ProjectEntity project = projectRepository.findById(projectId) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); + +// 프로젝트 종료 기간 검증 +// if (project.getEndDate().isBefore(LocalDateTime.now())) { +// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +// } // 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ + + // 팀원인지 팀장인지 검증은 필요없음. - public void sendProjectInvite(String email, String message) { SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo(email); - mailMessage.setSubject("Project participation code"); + mailMessage.setSubject("여기엔 무엇을 채우는거지?"); mailMessage.setText(message); mailSender.send(mailMessage); } From 4759a3e1ec467d9e818ca68d412bda6f229b013a Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 16:57:18 +0900 Subject: [PATCH 07/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 59affc0..689160c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -64,16 +64,6 @@ jobs: authorization-uri: https://accounts.google.com/o/oauth2/auth token-uri: https://oauth2.googleapis.com/token user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo - server: - port: 443 - ssl: - enabled: true - key-store: /etc/letsencrypt/live/seamlessup.com/keystore.p12 - key-store-password: ${{ - secrets.KEYSTORE_PASSWORD - }} - key-store-type: PKCS12 - key-alias: tomcat EOF # Gradle 캐시 설정 From 1d03a5de42ba07dad7bf2a01d19b2a3689051a36 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 18:45:49 +0900 Subject: [PATCH 08/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 689160c..fcf04f7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -64,6 +64,8 @@ jobs: authorization-uri: https://accounts.google.com/o/oauth2/auth token-uri: https://oauth2.googleapis.com/token user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo + server: + port: 8080 EOF # Gradle 캐시 설정 From 6612cf32ec2297b006243001346ff019ceae5a34 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 18:56:08 +0900 Subject: [PATCH 09/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fcf04f7..7c903a6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -65,7 +65,7 @@ jobs: token-uri: https://oauth2.googleapis.com/token user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo server: - port: 8080 + port: 80 EOF # Gradle 캐시 설정 From e10d11ccb10de15f6b4b0e08123068caba332475 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 19:38:22 +0900 Subject: [PATCH 10/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7c903a6..689160c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -64,8 +64,6 @@ jobs: authorization-uri: https://accounts.google.com/o/oauth2/auth token-uri: https://oauth2.googleapis.com/token user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo - server: - port: 80 EOF # Gradle 캐시 설정 From d30e1a6cbe806cba4d459fbb1952bf25234422ec Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 20:26:36 +0900 Subject: [PATCH 11/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 689160c..b1efb9c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -55,7 +55,7 @@ jobs: }} redirect-uri: "https://${{ secrets.LIGHT_SAIL_IP - }}/login/oauth2/code/google" + }} scope: - email - profile From 8858dc039e059db7ab59d3a54f1a2e249855be96 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 20:35:12 +0900 Subject: [PATCH 12/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b1efb9c..c15c5b4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -126,7 +126,7 @@ jobs: sleep 1 else kill -15 $CURRENT_PID - sleep 5 + sleep 10 fi DEPLOY_PATH=/home/ubuntu/seamless/deploy/ From 27f019df138df40c55040195cb2dacdee41f299e Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 20:48:30 +0900 Subject: [PATCH 13/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c15c5b4..9109c93 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,7 +53,7 @@ jobs: client-secret: ${{ secrets.GOOGLE_CLIENT_SECRET }} - redirect-uri: "https://${{ + redirect-uri: https://${{ secrets.LIGHT_SAIL_IP }} scope: From dee7facf0a114054e9a31af45ccc0a2fbf20d7d8 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Sat, 12 Oct 2024 20:59:37 +0900 Subject: [PATCH 14/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9109c93..ebf4e42 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -55,7 +55,7 @@ jobs: }} redirect-uri: https://${{ secrets.LIGHT_SAIL_IP - }} + }}/login/oauth2/code/google scope: - email - profile From 1a58ba70d4e9be94376bb08b78b93b7d9160f823 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Mon, 14 Oct 2024 18:22:39 +0900 Subject: [PATCH 15/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ebf4e42..bf8f421 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,9 +53,7 @@ jobs: client-secret: ${{ secrets.GOOGLE_CLIENT_SECRET }} - redirect-uri: https://${{ - secrets.LIGHT_SAIL_IP - }}/login/oauth2/code/google + redirect-uri: https://seamlessup.com/login/oauth2/code/google scope: - email - profile From e3b8c4f14807f28c7aa72777d726e4e2d8910bef Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:54:02 +0900 Subject: [PATCH 16/63] =?UTF-8?q?docs:=20README.md=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63be885..8bbb4d7 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ --- -# 그라운드 룰 +# 프로젝트 구조도 -- 추가 요망 +![seamless.png](..%2F..%2FDownloads%2Fseamless.png) --- From e907bb9c04d3f3789fb08eb2726588fbb964c5fb Mon Sep 17 00:00:00 2001 From: kim dong hyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:55:16 +0900 Subject: [PATCH 17/63] =?UTF-8?q?docs:=20README.md=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80(=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8bbb4d7..909ec3e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ # 프로젝트 구조도 -![seamless.png](..%2F..%2FDownloads%2Fseamless.png) +![seamless](https://github.com/user-attachments/assets/5d4c9a76-8c25-4af9-bcb4-c598d98e4de3) + --- @@ -150,4 +151,4 @@ - week5 - softDelete의 구현 방법 - 연결괸 객체의 정보를 가져오는 방법 -- week6 \ No newline at end of file +- week6 From b522260f7cde167e7c125651b76fe4f757d6605c Mon Sep 17 00:00:00 2001 From: kim dong hyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:57:58 +0900 Subject: [PATCH 18/63] =?UTF-8?q?docs:=20README.md=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 909ec3e..11a823c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ # 프로젝트 구조도 -![seamless](https://github.com/user-attachments/assets/5d4c9a76-8c25-4af9-bcb4-c598d98e4de3) +![seamless](https://github.com/user-attachments/assets/98d62cf5-8755-4980-9bea-a5ada9719ee7) + --- From 2b0a4873a0dc09936a3498a70aca6af2b1395ee5 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:08:51 +0900 Subject: [PATCH 19/63] =?UTF-8?q?fix:=20@Valid=EB=8A=94=20contoller?= =?UTF-8?q?=EC=97=90=EC=84=9C=EB=A7=8C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/service/TaskService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index 5ca6828..eb58e73 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -54,7 +54,7 @@ public Page getTaskList(Long projectId, getList param) { return taskEntities.map(taskMapper::toDetail); } - public TaskDetail createTask(HttpServletRequest req, @Valid Long projectId, Create create) { + public TaskDetail createTask(HttpServletRequest req, Long projectId, Create create) { ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( projectId, parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); @@ -76,7 +76,7 @@ public TaskDetail createTask(HttpServletRequest req, @Valid Long projectId, Crea } @Transactional - public TaskDetail updateTask(HttpServletRequest req, @Valid Long taskId, @Valid Update update) { + public TaskDetail updateTask(HttpServletRequest req, Long taskId, Update update) { TaskEntity task = taskRepository.findByIdAndIsDeletedFalse(taskId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 태스크")); From 3284acacecc441acd267eef8c8278a7705036617 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:11:24 +0900 Subject: [PATCH 20/63] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team1/BE/seamless/controller/AttendUrlController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/team1/BE/seamless/controller/AttendUrlController.java b/src/main/java/team1/BE/seamless/controller/AttendUrlController.java index 0d21292..fe5fb0b 100644 --- a/src/main/java/team1/BE/seamless/controller/AttendUrlController.java +++ b/src/main/java/team1/BE/seamless/controller/AttendUrlController.java @@ -39,10 +39,10 @@ public AttendUrlController(AttendURLService attendURLService) { * less) */ @Operation(summary = "팀원초대 링크 생성") - @PostMapping("/api/project/{project_id}/invite-link/{user-id}") + @PostMapping("/api/project/{projectId}/invite-link/{userId}") public SingleResult generateInviteLink(HttpServletRequest req, - @Valid @PathVariable("project_id") Long projectId, - @Valid @PathVariable("user-id") Long userId) { + @Valid @PathVariable("projectId") Long projectId, + @Valid @PathVariable("userId") Long userId) { return new SingleResult<>(attendURLService.generateAttendURL(req, projectId, userId)); } } \ No newline at end of file From 20d5769568423e6cd8116d6d04ae8a9a4f124b6f Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:12:10 +0900 Subject: [PATCH 21/63] =?UTF-8?q?fix:=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/TaskDTO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java index c010de9..697e487 100644 --- a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java @@ -79,7 +79,7 @@ public Update(String name, String remark, Integer progress, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { if (endDate.isBefore(startDate)) { - throw new BaseHandler(HttpStatus.FORBIDDEN, "종료시간은 시작시간보다 이전일 수 없습니다."); + throw new BaseHandler(HttpStatus.BAD_REQUEST, "종료시간은 시작시간보다 이전일 수 없습니다."); } this.name = name; this.remark = remark; From 8cf73673555dc0838acfc4ebe13cd675e27b95e2 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:15:50 +0900 Subject: [PATCH 22/63] =?UTF-8?q?fix:=20@valid=EB=8A=94=20controller?= =?UTF-8?q?=EC=97=90=EC=84=9C=EB=A7=8C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/service/UserService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/team1/BE/seamless/service/UserService.java b/src/main/java/team1/BE/seamless/service/UserService.java index 791b69e..261d1d3 100644 --- a/src/main/java/team1/BE/seamless/service/UserService.java +++ b/src/main/java/team1/BE/seamless/service/UserService.java @@ -37,7 +37,7 @@ public UserDetails getUser(HttpServletRequest req) { } @Transactional - public UserSimple updateUser(HttpServletRequest req, @Valid UserUpdate update) { + public UserSimple updateUser(HttpServletRequest req, UserUpdate update) { UserEntity user = userRepository.findByEmailAndIsDeleteFalse(parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 유저가 존재하지 않습니다.")); @@ -57,7 +57,7 @@ public UserSimple deleteUser(HttpServletRequest req) { } @Transactional - public UserEntity createUser(@Valid UserSimple simple) { + public UserEntity createUser( UserSimple simple) { return userRepository.findByEmailAndIsDeleteFalse(simple.getEmail()) .orElseGet(() -> userRepository.save( userMapper.toEntity( From 2c7710a1f738659559e131d6821752417b459e0d Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:48:27 +0900 Subject: [PATCH 23/63] =?UTF-8?q?fix:=20@Patter=EC=97=90=EC=84=9C=20@URL?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/UserDTO.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/UserDTO.java b/src/main/java/team1/BE/seamless/DTO/UserDTO.java index 8af7845..8377815 100644 --- a/src/main/java/team1/BE/seamless/DTO/UserDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/UserDTO.java @@ -2,6 +2,7 @@ import jakarta.validation.constraints.Pattern; import java.time.LocalDateTime; +import org.hibernate.validator.constraints.URL; public class UserDTO { @@ -9,8 +10,7 @@ public static class UserUpdate { private String username; - @Pattern(regexp = "^(https?:\\/\\/)?([a-zA-Z0-9_-]+\\.)+[a-zA-Z]{2,6}(:[0-9]{1,5})?(\\/.*)?$", - message = "사진은 URL이여야 합니다.") + @URL(protocol = "https", message = "사진은 URL이여야 합니다.") private String picture; public UserUpdate() { From e0e67b4061062d164ce0343160c88f6ef800aad0 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:48:44 +0900 Subject: [PATCH 24/63] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/service/UserServiceTest.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/test/java/team1/BE/seamless/service/UserServiceTest.java diff --git a/src/test/java/team1/BE/seamless/service/UserServiceTest.java b/src/test/java/team1/BE/seamless/service/UserServiceTest.java new file mode 100644 index 0000000..6feb277 --- /dev/null +++ b/src/test/java/team1/BE/seamless/service/UserServiceTest.java @@ -0,0 +1,89 @@ +package team1.BE.seamless.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import team1.BE.seamless.DTO.UserDTO.UserUpdate; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class UserServiceTest { + + @LocalServerPort + private int port; + private String url = "http://localhost:"; + private final TestRestTemplate restTemplate; + private String token; + private HttpHeaders headers = new HttpHeaders(); + + private final UserService userService; + + + @Autowired + public UserServiceTest(TestRestTemplate restTemplate, UserService userService) { + this.restTemplate = restTemplate; + this.userService = userService; + } + + /** + * 특정 유저id의 토큰 파싱 + */ + @BeforeEach + public void setUp() { + HttpEntity requestEntity = new HttpEntity<>(null); + ResponseEntity responseEntity = restTemplate.exchange( + url + port + "/api/test/userToken/1", + POST, + requestEntity, String.class); + + int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + int endIndex = responseEntity.getBody().indexOf("\"", startIndex); + + token = responseEntity.getBody().substring(startIndex, endIndex); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(token); + } + + @Test + void 유저정보_수정_테스트_실패_url() { + UserUpdate body = new UserUpdate("name1","qwer1234"); + + HttpEntity requestEntity = new HttpEntity(body, headers); + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/user", + PUT, + requestEntity, String.class); + + System.out.println(responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + } + + @Test + void 유저정보_삭제_실패() { + HttpEntity requestEntity = new HttpEntity(null, headers); +// 삭제 두 번 시도 + restTemplate.exchange(url + port + "/api/user", + DELETE, + requestEntity, String.class); + + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/user", + DELETE, + requestEntity, String.class); + + System.out.println(responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + } +} \ No newline at end of file From b383b5eb6a6f4dfa1ade162efcaf224bb0cdc786 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Tue, 15 Oct 2024 18:59:36 +0900 Subject: [PATCH 25/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/util/auth/CookieUtils.java | 57 +++++++++++++++++++ ...eOAuth2AuthorizationRequestRepository.java | 50 ++++++++++++++++ .../BE/seamless/util/auth/SecurityConfig.java | 21 +++++-- 3 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/main/java/team1/BE/seamless/util/auth/CookieUtils.java create mode 100644 src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java diff --git a/src/main/java/team1/BE/seamless/util/auth/CookieUtils.java b/src/main/java/team1/BE/seamless/util/auth/CookieUtils.java new file mode 100644 index 0000000..3f4ca7b --- /dev/null +++ b/src/main/java/team1/BE/seamless/util/auth/CookieUtils.java @@ -0,0 +1,57 @@ +package team1.BE.seamless.util.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Base64; +import java.util.Optional; +import org.springframework.util.SerializationUtils; + + +public class CookieUtils { + + public static Optional getCookie(HttpServletRequest request, String name) { + Cookie[] cookies = request.getCookies(); + if (cookies != null && cookies.length > 0) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + return Optional.of(cookie); + } + } + } + return Optional.empty(); + } + + public static void addCookie(HttpServletResponse response, String name, String value, + int maxAge) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + cookie.setHttpOnly(true); + cookie.setMaxAge(maxAge); + response.addCookie(cookie); + } + + public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, + String name) { + Cookie[] cookies = request.getCookies(); + if (cookies != null && cookies.length > 0) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + cookie.setValue(""); + cookie.setPath("/"); + cookie.setMaxAge(0); + response.addCookie(cookie); + } + } + } + } + + public static String serialize(Object object) { + return Base64.getUrlEncoder().encodeToString(SerializationUtils.serialize(object)); + } + + public static T deserialize(Cookie cookie, Class cls) { + return cls.cast( + SerializationUtils.deserialize(Base64.getUrlDecoder().decode(cookie.getValue()))); + } +} \ No newline at end of file diff --git a/src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java b/src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java new file mode 100644 index 0000000..0c5ccd1 --- /dev/null +++ b/src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java @@ -0,0 +1,50 @@ +package team1.BE.seamless.util.auth; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.stereotype.Component; + +@Component +public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository { + + public static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request"; + public static final String REDIRECT_URI_PARAM_COOKIE_NAME = "redirect_uri"; + private static final int cookieExpireSeconds = 180; + + @Override + public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) { + OAuth2AuthorizationRequest oAuth2AuthorizationRequest = CookieUtils.getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME) + .map(cookie -> CookieUtils.deserialize(cookie, OAuth2AuthorizationRequest.class)) + .orElse(null); + return oAuth2AuthorizationRequest; + } + + @Override + public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) { + if (authorizationRequest == null) { + removeAuthorizationRequest(request, response); + return; + } + + CookieUtils.addCookie(response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, CookieUtils.serialize(authorizationRequest), cookieExpireSeconds); + String redirectUriAfterLogin = request.getParameter(REDIRECT_URI_PARAM_COOKIE_NAME); + if (StringUtils.isNotBlank(redirectUriAfterLogin)) { + CookieUtils.addCookie(response, REDIRECT_URI_PARAM_COOKIE_NAME, redirectUriAfterLogin, cookieExpireSeconds); + } + } + + @Override + public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request, + HttpServletResponse response) { + return this.loadAuthorizationRequest(request); + } + + public void removeAuthorizationRequestCookies(HttpServletRequest request, HttpServletResponse response) { + CookieUtils.deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME); + CookieUtils.deleteCookie(request, response, REDIRECT_URI_PARAM_COOKIE_NAME); + } + +} \ No newline at end of file diff --git a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java index 479e04a..6265af7 100644 --- a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java +++ b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java @@ -3,6 +3,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -23,22 +24,26 @@ public class SecurityConfig { private final TokenAuthenticationFilter tokenAuthenticationFilter; private final TokenExceptionFilter tokenExceptionFilter; private final SecurityEntryPoint SecurityException; + private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository; @Autowired public SecurityConfig(AuthService authService, OAuth2SuccessHandler successHandler, TokenAuthenticationFilter tokenAuthenticationFilter, TokenExceptionFilter tokenExceptionFilter, - SecurityEntryPoint SecurityException) { + SecurityEntryPoint securityException, + HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) { this.authService = authService; this.successHandler = successHandler; this.tokenAuthenticationFilter = tokenAuthenticationFilter; this.tokenExceptionFilter = tokenExceptionFilter; - this.SecurityException = SecurityException; + SecurityException = securityException; + this.authorizationRequestRepository = authorizationRequestRepository; } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http, + HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .cors(AbstractHttpConfigurer::disable) @@ -59,7 +64,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/**/*.svg", "/**/*.jpg", "/**/*.html", "/**/*.css", "/**/*.js") .permitAll() // 인증, h2 - .requestMatchers("/h2-console/","/auth/**").permitAll() + .requestMatchers("/h2-console/**", "/auth/**","/api/test/**").permitAll() +// 멤버 생성 + .requestMatchers(HttpMethod.POST,"/api/project/{project_id}/member/**").permitAll() +// 멤버 조회 + .requestMatchers(HttpMethod.GET,"/api/project/{project_id}/member/**").permitAll() .anyRequest() .authenticated() ) @@ -67,8 +76,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .oauth2Login(oauth -> oauth .userInfoEndpoint(c -> c.userService(authService)) .successHandler(successHandler) + .authorizationEndpoint() + .baseUri("/login/oauth2/code/*") + .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository) ) + // .exceptionHandling(handler -> handler.authenticationEntryPoint(SecurityException)) .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) From e5ab86ca7a1b7c1cfa2add3d6bcdeb5cde6ded1e Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Tue, 15 Oct 2024 19:40:01 +0900 Subject: [PATCH 26/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/entity/UserEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/team1/BE/seamless/entity/UserEntity.java b/src/main/java/team1/BE/seamless/entity/UserEntity.java index 618ed85..cb9fea0 100644 --- a/src/main/java/team1/BE/seamless/entity/UserEntity.java +++ b/src/main/java/team1/BE/seamless/entity/UserEntity.java @@ -18,7 +18,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener; import team1.BE.seamless.entity.enums.Role; -@Entity(name = "user_table") +@Entity(name = "userss") @EntityListeners(AuditingEntityListener.class) public class UserEntity { From dc71800f55b1c047c6cba6b316de360b7aab576b Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:49:02 +0900 Subject: [PATCH 27/63] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=98=B5=EC=85=98=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team1/BE/seamless/DTO/OptionDTO.java | 112 ++++++++++++++++++ .../controller/ProjectOptionController.java | 76 ++++++++++++ .../BE/seamless/entity/OptionEntity.java | 30 +++++ .../BE/seamless/mapper/OptionMapper.java | 23 +++- .../seamless/repository/OptionRepository.java | 7 ++ .../BE/seamless/service/OptionService.java | 67 +++++++++++ 6 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 src/main/java/team1/BE/seamless/controller/ProjectOptionController.java diff --git a/src/main/java/team1/BE/seamless/DTO/OptionDTO.java b/src/main/java/team1/BE/seamless/DTO/OptionDTO.java index 59a542a..7264d4b 100644 --- a/src/main/java/team1/BE/seamless/DTO/OptionDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/OptionDTO.java @@ -1,15 +1,23 @@ package team1.BE.seamless.DTO; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; import team1.BE.seamless.entity.enums.OptionType; +import team1.BE.seamless.util.page.PageParam; public class OptionDTO { + public static class getList extends PageParam { + + } public static class OptionCreate { + @NotNull private String name; private String description; + @NotNull private String optionType; public OptionCreate() { @@ -33,7 +41,111 @@ public String getDescription() { public String getOptionType() { return optionType; } + } + + public static class updateOption { + + private String name; + + private String description; + + private String optionType; + + public updateOption() { + + } + + public updateOption(String name, String description, String optionType) { + this.name = name; + this.description = description; + this.optionType = optionType; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getOptionType() { + return optionType; + } + + } + + public static class OptionSimple{ + private Long id; + private String name; + private String optionType; + + public OptionSimple() { + } + + public OptionSimple(Long id, String name, OptionType optionType) { + this.id = id; + this.name = name; + this.optionType = optionType.toString(); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getOptionType() { + return optionType; + } + } + + public static class OptionDetail{ + private Long id; + private String name; + private String description; + private String optionType; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + public OptionDetail() { + } + + public OptionDetail(Long id, String name, String description, OptionType optionType, + LocalDateTime createdAt, LocalDateTime updatedAt) { + this.id = id; + this.name = name; + this.description = description; + this.optionType = optionType.toString(); + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getOptionType() { + return optionType; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + public LocalDateTime getUpdatedAt() { + return updatedAt; + } } } diff --git a/src/main/java/team1/BE/seamless/controller/ProjectOptionController.java b/src/main/java/team1/BE/seamless/controller/ProjectOptionController.java new file mode 100644 index 0000000..130bd66 --- /dev/null +++ b/src/main/java/team1/BE/seamless/controller/ProjectOptionController.java @@ -0,0 +1,76 @@ +package team1.BE.seamless.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import team1.BE.seamless.DTO.OptionDTO; +import team1.BE.seamless.DTO.OptionDTO.OptionDetail; +import team1.BE.seamless.DTO.OptionDTO.OptionSimple; +import team1.BE.seamless.service.OptionService; +import team1.BE.seamless.util.auth.ParsingPram; +import team1.BE.seamless.util.page.PageMapper; +import team1.BE.seamless.util.page.PageResult; +import team1.BE.seamless.util.page.SingleResult; + +@Tag(name = "프로젝트 옵션") +@RestController +@RequestMapping("/api/project/option") +public class ProjectOptionController { + + private final OptionService optionService; + private final ParsingPram parsingPram; + + @Autowired + public ProjectOptionController(OptionService optionService, ParsingPram parsingPram) { + this.optionService = optionService; + this.parsingPram = parsingPram; + } + + @Operation(summary = "프로젝트 옵션 리스트 조회") + @GetMapping + public PageResult getOptionList(HttpServletRequest req, + @Valid OptionDTO.getList param) { + return PageMapper.toPageResult( + optionService.getProjectOptionList(param, parsingPram.getRole(req))); + } + + @Operation(summary = "프로젝트 옵션 조회") + @GetMapping("/{optionId}") + public SingleResult getOption(HttpServletRequest req, + @Valid @PathVariable("optionId") Long id) { + return new SingleResult<>(optionService.getOption(id, parsingPram.getRole(req))); + } + + @Operation(summary = "옵션 생성") + @PostMapping + public SingleResult createOption(HttpServletRequest req, + @Valid @RequestBody OptionDTO.OptionCreate create) { + return new SingleResult<>(optionService.createOption(create, parsingPram.getRole(req))); + } + + @Operation(summary = "옵션 수정") + @PutMapping("/{optionId}") + public SingleResult updateOption(HttpServletRequest req, + @Valid @PathVariable("optionId") Long id, + @Valid @RequestBody OptionDTO.updateOption update) { + return new SingleResult<>(optionService.updateOption(id, update, parsingPram.getRole(req))); + } + + @Operation(summary = "옵션 수정") + @DeleteMapping("/{optionId}") + public SingleResult deleteOption(HttpServletRequest req, + @Valid @PathVariable("optionId") Long id) { + return new SingleResult<>(optionService.deleteOption(id, parsingPram.getRole(req))); + } + +} diff --git a/src/main/java/team1/BE/seamless/entity/OptionEntity.java b/src/main/java/team1/BE/seamless/entity/OptionEntity.java index f822604..c7245b7 100644 --- a/src/main/java/team1/BE/seamless/entity/OptionEntity.java +++ b/src/main/java/team1/BE/seamless/entity/OptionEntity.java @@ -9,7 +9,10 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.OneToMany; +import java.time.LocalDateTime; import java.util.List; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; import team1.BE.seamless.entity.enums.OptionType; @Entity(name = "option") @@ -47,6 +50,12 @@ public OptionEntity(String name, String description, OptionType optionType) { @OneToMany(mappedBy = "optionEntity", cascade = CascadeType.ALL) private List options; + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; + public Long getId() { return id; } @@ -71,6 +80,20 @@ public List getOptions() { return options; } + public boolean isDeleted() { + return isDeleted; + } + + @Override + public LocalDateTime getCreatedAt() { + return createdAt; + } + + @Override + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + public void setOptions(List options) { this.options = options; } @@ -79,4 +102,11 @@ public void setIsDeleted(boolean isDeleted) { this.isDeleted = isDeleted; } + public OptionEntity Update(String name, String description, OptionType optionType) { + this.name = name; + this.description=description; + this.optionType = optionType; + return this; + } + } diff --git a/src/main/java/team1/BE/seamless/mapper/OptionMapper.java b/src/main/java/team1/BE/seamless/mapper/OptionMapper.java index e00976a..de95b8a 100644 --- a/src/main/java/team1/BE/seamless/mapper/OptionMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/OptionMapper.java @@ -3,8 +3,12 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.OptionDTO.OptionCreate; +import team1.BE.seamless.DTO.OptionDTO.OptionDetail; +import team1.BE.seamless.DTO.OptionDTO.OptionSimple; +import team1.BE.seamless.DTO.OptionDTO.updateOption; import team1.BE.seamless.entity.OptionEntity; import team1.BE.seamless.entity.enums.OptionType; +import team1.BE.seamless.util.Util; import team1.BE.seamless.util.errorException.BaseHandler; @Component @@ -18,12 +22,29 @@ public OptionEntity toEntity(OptionCreate create) { ); } + public OptionEntity toUpdate(OptionEntity entity, updateOption update) { + return entity.Update( + Util.isNull(update.getName()) ? entity.getName() : update.getName(), + Util.isNull(update.getDescription())? entity.getDescription() : update.getDescription(), + Util.isNull(update.getOptionType().toString())?entity.getOptionType():toOptionType(update.getOptionType()) + ); + } + public OptionType toOptionType(String optionType) { for (OptionType type : OptionType.values()) { if (type.getKey().equals(optionType)) { return type; } } - throw new BaseHandler(HttpStatus.NOT_FOUND,"잘못된 옵션 타입입니다."); + throw new BaseHandler(HttpStatus.NOT_FOUND, "잘못된 옵션 타입입니다."); + } + + public OptionSimple toSimple(OptionEntity entity) { + return new OptionSimple(entity.getId(), entity.getName(), entity.getOptionType()); + } + + public OptionDetail toDetail(OptionEntity entity) { + return new OptionDetail(entity.getId(), entity.getName(), entity.getDescription(), + entity.getOptionType(), entity.getCreatedAt(), entity.getUpdatedAt()); } } diff --git a/src/main/java/team1/BE/seamless/repository/OptionRepository.java b/src/main/java/team1/BE/seamless/repository/OptionRepository.java index 434e80d..89f4516 100644 --- a/src/main/java/team1/BE/seamless/repository/OptionRepository.java +++ b/src/main/java/team1/BE/seamless/repository/OptionRepository.java @@ -2,6 +2,9 @@ import java.util.List; +import java.util.Optional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import team1.BE.seamless.entity.OptionEntity; @@ -11,4 +14,8 @@ public interface OptionRepository extends JpaRepository { List findByIdIn(List ids); + Page findAllByIsDeletedFalse(Pageable pageable); + + Optional findByIdAndIsDeletedFalse(Long id); + } diff --git a/src/main/java/team1/BE/seamless/service/OptionService.java b/src/main/java/team1/BE/seamless/service/OptionService.java index d984f56..ceb917d 100644 --- a/src/main/java/team1/BE/seamless/service/OptionService.java +++ b/src/main/java/team1/BE/seamless/service/OptionService.java @@ -1,12 +1,20 @@ package team1.BE.seamless.service; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import team1.BE.seamless.DTO.OptionDTO.OptionCreate; +import team1.BE.seamless.DTO.OptionDTO.OptionDetail; +import team1.BE.seamless.DTO.OptionDTO.OptionSimple; +import team1.BE.seamless.DTO.OptionDTO.getList; +import team1.BE.seamless.DTO.OptionDTO.updateOption; import team1.BE.seamless.entity.OptionEntity; +import team1.BE.seamless.entity.enums.Role; import team1.BE.seamless.mapper.OptionMapper; import team1.BE.seamless.repository.OptionRepository; +import team1.BE.seamless.util.errorException.BaseHandler; @Service public class OptionService { @@ -21,8 +29,67 @@ public OptionService(OptionRepository optionRepository, OptionMapper optionMappe this.optionMapper = optionMapper; } + public Page getProjectOptionList(getList param, String role) { + if (!role.equals(Role.USER.getKey())) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "로그인한 유저만 조회 가능합니다."); + } + + return optionRepository.findAllByIsDeletedFalse(param.toPageable()).map(optionMapper::toSimple); + } + + public OptionDetail getOption(Long id, String role) { + if (!role.equals(Role.USER.getKey())) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "로그인한 유저만 조회 가능합니다."); + } + + return optionMapper.toDetail(optionRepository.findByIdAndIsDeletedFalse(id) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당하는 옵션이 존재하지 않습니다."))); + } + + @Transactional + public OptionDetail createOption(OptionCreate create, String role) { + if (!role.equals(Role.USER.getKey())) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "로그인한 유저만 생성 가능합니다."); + } + OptionEntity optionEntity = optionMapper.toEntity(create); + optionRepository.save(optionEntity); + + return optionMapper.toDetail(optionEntity); + } + + @Transactional + public OptionDetail updateOption(Long id, updateOption update, String role) { + if (!role.equals(Role.USER.getKey())) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "로그인한 유저만 수정 가능합니다."); + } + OptionEntity option = optionRepository.findByIdAndIsDeletedFalse(id) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당하는 옵션이 존재하지 않습니다.")); + + optionMapper.toUpdate(option, update); + + return optionMapper.toDetail(option); + } + + @Transactional + public OptionDetail deleteOption(Long id, String role) { + if (!role.equals(Role.USER.getKey())) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "로그인한 유저만 삭제 가능합니다."); + } + + OptionEntity option = optionRepository.findByIdAndIsDeletedFalse(id) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당하는 옵션이 존재하지 않습니다.")); + + option.setIsDeleted(true); + + return optionMapper.toDetail(option); + } + + /** + * 테스트용 + */ @Transactional public OptionEntity createOption(OptionCreate create) { return optionRepository.save(optionMapper.toEntity(create)); } + } From 9740f6bec5a6270fd17a1f09e1f0a5807ee34846 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Tue, 15 Oct 2024 19:52:44 +0900 Subject: [PATCH 28/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/entity/MemberEntity.java | 2 +- src/main/java/team1/BE/seamless/entity/OptionEntity.java | 2 +- src/main/java/team1/BE/seamless/entity/ProjectEntity.java | 2 +- src/main/java/team1/BE/seamless/entity/ProjectOption.java | 2 +- src/main/java/team1/BE/seamless/entity/TaskEntity.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/team1/BE/seamless/entity/MemberEntity.java b/src/main/java/team1/BE/seamless/entity/MemberEntity.java index 20544e8..adcf0dd 100644 --- a/src/main/java/team1/BE/seamless/entity/MemberEntity.java +++ b/src/main/java/team1/BE/seamless/entity/MemberEntity.java @@ -12,7 +12,7 @@ import java.util.ArrayList; import java.util.List; -@Entity(name = "member") +@Entity(name = "memberss") public class MemberEntity extends BaseEntity { public MemberEntity() { diff --git a/src/main/java/team1/BE/seamless/entity/OptionEntity.java b/src/main/java/team1/BE/seamless/entity/OptionEntity.java index f822604..3ca6327 100644 --- a/src/main/java/team1/BE/seamless/entity/OptionEntity.java +++ b/src/main/java/team1/BE/seamless/entity/OptionEntity.java @@ -12,7 +12,7 @@ import java.util.List; import team1.BE.seamless.entity.enums.OptionType; -@Entity(name = "option") +@Entity(name = "optionss") public class OptionEntity extends BaseEntity { public OptionEntity() { diff --git a/src/main/java/team1/BE/seamless/entity/ProjectEntity.java b/src/main/java/team1/BE/seamless/entity/ProjectEntity.java index 9480ac6..c68a148 100644 --- a/src/main/java/team1/BE/seamless/entity/ProjectEntity.java +++ b/src/main/java/team1/BE/seamless/entity/ProjectEntity.java @@ -14,7 +14,7 @@ import java.util.ArrayList; import java.util.List; -@Entity(name = "project") +@Entity(name = "projectss") public class ProjectEntity extends BaseEntity { public ProjectEntity() { diff --git a/src/main/java/team1/BE/seamless/entity/ProjectOption.java b/src/main/java/team1/BE/seamless/entity/ProjectOption.java index ba45c6c..097287d 100644 --- a/src/main/java/team1/BE/seamless/entity/ProjectOption.java +++ b/src/main/java/team1/BE/seamless/entity/ProjectOption.java @@ -9,7 +9,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -@Entity(name = "project_option") +@Entity(name = "project_optionss") public class ProjectOption extends BaseEntity { public ProjectOption() { diff --git a/src/main/java/team1/BE/seamless/entity/TaskEntity.java b/src/main/java/team1/BE/seamless/entity/TaskEntity.java index a9356b7..7bc1cce 100644 --- a/src/main/java/team1/BE/seamless/entity/TaskEntity.java +++ b/src/main/java/team1/BE/seamless/entity/TaskEntity.java @@ -10,7 +10,7 @@ import jakarta.persistence.ManyToOne; import java.time.LocalDateTime; -@Entity(name = "task") +@Entity(name = "taskss") public class TaskEntity { public TaskEntity() { From 211d676dccd08f8453c4b600e76ad391a23f343c Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Tue, 15 Oct 2024 20:01:43 +0900 Subject: [PATCH 29/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6079bcb..316f419 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -13,7 +13,7 @@ spring.jpa.database=h2 spring.jpa.defer-datasource-initialization=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect jwt.secretKey=KO)$*"g+zWechI9]KE|'irIM?ky--x/|p.K&>bA>Ef,gfD/)ekd/UcBE2kQ@BhE jwt.tokenExpTime=3600 code.secretKey=ae2af8cfb8721e39189f6f5edc928589 From f9dfdb5497740164477d61154284096728ff9d21 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Tue, 15 Oct 2024 20:22:03 +0900 Subject: [PATCH 30/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bf8f421..0981716 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -124,11 +124,12 @@ jobs: sleep 1 else kill -15 $CURRENT_PID + wait $CURRENT_PID sleep 10 fi DEPLOY_PATH=/home/ubuntu/seamless/deploy/ - mkdir $DEPLOY_PATH + mkdir -p $DEPLOY_PATH cp $BUILD_PATH $DEPLOY_PATH cd $DEPLOY_PATH From f2347ff99fe8efd510e233ca48ffcc417c1f6001 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Tue, 15 Oct 2024 22:38:46 +0900 Subject: [PATCH 31/63] =?UTF-8?q?test:=20=ED=83=9C=EC=8A=A4=ED=81=AC=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=EC=8B=9C=EA=B0=84=EC=9D=B4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=9D=BC=EC=A0=95=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=EB=B3=B4=EB=8B=A4=20=EC=9D=B4=EB=A5=BC=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=8B=A4=ED=8C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/service/TaskServiceTest.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/test/java/team1/BE/seamless/service/TaskServiceTest.java diff --git a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java new file mode 100644 index 0000000..c0c3960 --- /dev/null +++ b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java @@ -0,0 +1,92 @@ +package team1.BE.seamless.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.FORBIDDEN; + +import jakarta.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import team1.BE.seamless.DTO.ProjectDTO.ProjectCreate; +import team1.BE.seamless.DTO.TaskDTO.Create; +import team1.BE.seamless.entity.MemberEntity; +import team1.BE.seamless.entity.ProjectEntity; +import team1.BE.seamless.entity.UserEntity; +import team1.BE.seamless.repository.MemberRepository; +import team1.BE.seamless.repository.ProjectRepository; +import team1.BE.seamless.repository.TaskRepository; +import team1.BE.seamless.repository.UserRepository; +import team1.BE.seamless.util.errorException.BaseHandler; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class TaskServiceTest { + + @LocalServerPort + private int port; + private String url = "http://localhost:"; + private final TestRestTemplate restTemplate; + private String token; + private HttpHeaders headers = new HttpHeaders(); + + private TaskService taskService; + private ProjectRepository projectRepository; + private UserRepository userRepository; + private MemberRepository memberRepository; + + @Autowired + public TaskServiceTest(TestRestTemplate restTemplate, TaskService taskService, ProjectRepository projectRepository, UserRepository userRepository, MemberRepository memberRepository) { + this.restTemplate = restTemplate; + this.taskService = taskService; + this.projectRepository = projectRepository; + this.userRepository = userRepository; + this.memberRepository = memberRepository; + } + + @BeforeEach + public void setUp() { + HttpEntity requestEntity = new HttpEntity<>(null); + ResponseEntity responseEntity = restTemplate.exchange( + url + port + "/api/test/userToken/1", + POST, + requestEntity, String.class); + + int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + int endIndex = responseEntity.getBody().indexOf("\"", startIndex); + + token = responseEntity.getBody().substring(startIndex, endIndex); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(token); + } + + @Test + public void 태스크_시작_시간이_프로젝트_일정_범위보다_이를_경우_실패() { + Optional project = projectRepository.findByIdAndIsDeletedFalse(1L); + + Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2023, 12, 1, 0, 0), LocalDateTime.of(2024, 5, 3, 1, 0, 0)); + + HttpEntity requestEntity = new HttpEntity(body, headers); + + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1/task", POST, requestEntity, String.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(FORBIDDEN); + } + +} \ No newline at end of file From 5c598a56a4da2cbd339a795d452e4fdf358d0f89 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Tue, 15 Oct 2024 22:41:49 +0900 Subject: [PATCH 32/63] =?UTF-8?q?test:=20=ED=83=9C=EC=8A=A4=ED=81=AC=20?= =?UTF-8?q?=EB=A7=88=EA=B0=90=20=EC=8B=9C=EA=B0=84=EC=9D=B4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=9D=BC=EC=A0=95=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=EB=B3=B4=EB=8B=A4=20=EB=8A=A6=EC=9D=84=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=EC=8B=A4=ED=8C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/service/TaskServiceTest.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java index c0c3960..46dfc3f 100644 --- a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java +++ b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java @@ -24,16 +24,11 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit.jupiter.SpringExtension; -import team1.BE.seamless.DTO.ProjectDTO.ProjectCreate; import team1.BE.seamless.DTO.TaskDTO.Create; -import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; -import team1.BE.seamless.entity.UserEntity; import team1.BE.seamless.repository.MemberRepository; import team1.BE.seamless.repository.ProjectRepository; -import team1.BE.seamless.repository.TaskRepository; import team1.BE.seamless.repository.UserRepository; -import team1.BE.seamless.util.errorException.BaseHandler; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -80,6 +75,19 @@ public void setUp() { public void 태스크_시작_시간이_프로젝트_일정_범위보다_이를_경우_실패() { Optional project = projectRepository.findByIdAndIsDeletedFalse(1L); + Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2024, 10, 10, 0, 0), LocalDateTime.of(2026, 5, 3, 1, 0, 0)); + + HttpEntity requestEntity = new HttpEntity(body, headers); + + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1/task", POST, requestEntity, String.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(FORBIDDEN); + } + + @Test + public void 태스크_마감_시간이_프로젝트_일정_범위보다_늦을_경우_실패() { + Optional project = projectRepository.findByIdAndIsDeletedFalse(1L); + Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2023, 12, 1, 0, 0), LocalDateTime.of(2024, 5, 3, 1, 0, 0)); HttpEntity requestEntity = new HttpEntity(body, headers); From eca1c49eb6e14cb0a475e0fe926224bb7c13a253 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Wed, 16 Oct 2024 12:10:07 +0900 Subject: [PATCH 33/63] docs: README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 11a823c..7da9dca 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ - [4주차 리뷰](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/17) - [4주차 멘토링](https://quickest-asterisk-75d.notion.site/Back-end_-323b0e20ae2b405189ffe5b7c4242e00) - [5주차 리뷰](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/31) +- [6주차 피드백](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/pull/58) --- # Issue - [week4 프로젝트 빌드 실패](https://github.com/kakao-tech-campus-2nd-step3/Team1_BE/issues/27) From 21b3607837f56e906e3dada685c876ebd23e6644 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:52:24 +0900 Subject: [PATCH 34/63] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EC=88=98=EC=A0=95(?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/DTO/MemberRequestDTO.java | 73 +++++++++++++------ .../controller/AttendUrlController.java | 4 +- .../seamless/controller/MemberController.java | 7 +- .../team1/BE/seamless/init/MemberCreator.java | 27 ++++--- .../BE/seamless/mapper/MemberMapper.java | 6 +- .../seamless/repository/MemberRepository.java | 2 + .../BE/seamless/service/AttendURLService.java | 2 +- .../BE/seamless/service/AuthService.java | 8 +- .../BE/seamless/service/MemberService.java | 32 +++++++- 9 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java b/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java index 5c58e4b..5424559 100644 --- a/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java @@ -11,46 +11,71 @@ public static class getMemberList extends PageParam { } - public static class CreateMember { - - @NotBlank(message = "이름은 필수 입력 사항입니다.") - @Size(max = 15, message = "이름은 공백 포함 최대 15글자까지 가능합니다.") - private String name; +// public static class CreateMember { +// +// @NotBlank(message = "이름은 필수 입력 사항입니다.") +// @Size(max = 15, message = "이름은 공백 포함 최대 15글자까지 가능합니다.") +// private String name; +// +// private String role; +// +// @Email(message = "유효한 이메일 주소를 입력해주세요.") +// @NotBlank(message = "이메일은 필수 입력 사항입니다.") +// private String email; +// +// private String imageURL; +// +// +// public CreateMember() { +// } +// +// public CreateMember(String name, String role, String email, String imageURL) { +// this.name = name; +// this.role = role; +// this.email = email; +// this.imageURL = imageURL; +// } +// +// public String getName() { +// return name; +// } +// +// public String getRole() { +// return role; +// } +// +// public String getEmail() { +// return email; +// } +// +// public String getImageURL() { +// return imageURL; +// } +// } - @NotBlank(message = "역할은 필수 입력 사항입니다.") - @Size(max = 15, message = "역할은 공백 포함 최대 15글자까지 가능합니다.") - private String role; + public static class CreateMember { @Email(message = "유효한 이메일 주소를 입력해주세요.") @NotBlank(message = "이메일은 필수 입력 사항입니다.") private String email; - private String imageURL; + private String code; + public CreateMember() { } - public CreateMember(String name, String role, String email, String imageURL) { - this.name = name; - this.role = role; + public CreateMember(String email, String code) { this.email = email; - this.imageURL = imageURL; + this.code = code; } - public String getName() { - return name; - } - - public String getRole() { - return role; - } - - public String getEmail() { + public @Email(message = "유효한 이메일 주소를 입력해주세요.") @NotBlank(message = "이메일은 필수 입력 사항입니다.") String getEmail() { return email; } - public String getImageURL() { - return imageURL; + public String getCode() { + return code; } } diff --git a/src/main/java/team1/BE/seamless/controller/AttendUrlController.java b/src/main/java/team1/BE/seamless/controller/AttendUrlController.java index fe5fb0b..026fd22 100644 --- a/src/main/java/team1/BE/seamless/controller/AttendUrlController.java +++ b/src/main/java/team1/BE/seamless/controller/AttendUrlController.java @@ -35,10 +35,8 @@ public AttendUrlController(AttendURLService attendURLService) { /** * 팉장의 토큰과 프로젝트id로 프로젝트 존재 검증 프로젝트id + " " + exp로 코드 생성 코드를 양방향 암호화 ex) - * http://localhost:8080/api/memverInvite?code="123456" 검증시 코드를 복호화 해서 프로젝트id와 exp를 검증(server - * less) */ - @Operation(summary = "팀원초대 링크 생성") + @Operation(summary = "팀원초대 코드 생성") @PostMapping("/api/project/{projectId}/invite-link/{userId}") public SingleResult generateInviteLink(HttpServletRequest req, @Valid @PathVariable("projectId") Long projectId, diff --git a/src/main/java/team1/BE/seamless/controller/MemberController.java b/src/main/java/team1/BE/seamless/controller/MemberController.java index a0099ab..8ec33fd 100644 --- a/src/main/java/team1/BE/seamless/controller/MemberController.java +++ b/src/main/java/team1/BE/seamless/controller/MemberController.java @@ -56,11 +56,8 @@ public PageResult getMemberList(@Valid @PathVariable("project_id") @Operation(summary = "새 팀원 추가") @PostMapping - public SingleResult createMember( - @PathVariable("project_id") Long projectId, - @Valid @RequestBody MemberRequestDTO.CreateMember Create, - HttpServletRequest req) { - return new SingleResult<>(memberService.createMember(projectId, Create, req)); + public SingleResult createMember(@Valid @RequestBody MemberRequestDTO.CreateMember Create) { + return new SingleResult<>(memberService.createMember(Create)); } @Operation(summary = "팀원 정보 수정") diff --git a/src/main/java/team1/BE/seamless/init/MemberCreator.java b/src/main/java/team1/BE/seamless/init/MemberCreator.java index 07da476..97ab2d3 100644 --- a/src/main/java/team1/BE/seamless/init/MemberCreator.java +++ b/src/main/java/team1/BE/seamless/init/MemberCreator.java @@ -16,15 +16,24 @@ public MemberCreator(MemberService memberService) { } public void creator() { - // 테스트용으로 request데이터 생성 - MemberRequestDTO.CreateMember member1 = new MemberRequestDTO.CreateMember("권순호","MEMBER","ex1@gmail.com","exURL1"); - memberService.createMember(1L, member1); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 - - MemberRequestDTO.CreateMember member2 = new MemberRequestDTO.CreateMember("김동혁","USER","ex2@gmail.com","exURL2"); - memberService.createMember(1L, member2); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 - - MemberRequestDTO.CreateMember member3 = new MemberRequestDTO.CreateMember("김도헌","MEMBER","ex3@gmail.com","exURL3"); - memberService.createMember(1L, member3); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 +// // 테스트용으로 request데이터 생성 +// MemberRequestDTO.CreateMember member1 = new MemberRequestDTO.CreateMember("권순호","MEMBER","ex1@gmail.com","ZG4zyIfK/i2BNPoL4pYjbaasMQ9kZu2kuzj9VVcMuAD1g/vSWs+gt2doo4UpJmPR"); +// memberService.createMember(1L, member1); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 +// +// MemberRequestDTO.CreateMember member2 = new MemberRequestDTO.CreateMember("김동혁","USER","ex2@gmail.com","ZG4zyIfK/i2BNPoL4pYjbaasMQ9kZu2kuzj9VVcMuAD1g/vSWs+gt2doo4UpJmPR"); +// memberService.createMember(1L, member2); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 +// +// MemberRequestDTO.CreateMember member3 = new MemberRequestDTO.CreateMember("김도헌","MEMBER","ex3@gmail.com","ZG4zyIfK/i2BNPoL4pYjbaasMQ9kZu2kuzj9VVcMuAD1g/vSWs+gt2doo4UpJmPR"); +// memberService.createMember(1L, member3); // HttpServletRequest는 null 처리가 안돼서 테스트용으로 새로운 create만듦 + + MemberRequestDTO.CreateMember member1 = new MemberRequestDTO.CreateMember("ex1@gmail.com","cCeJvA99H7bV2ctvVIpM4Bh3ZJvawh3JnX3tREWGtNA="); + memberService.createMember(member1); + + MemberRequestDTO.CreateMember member2 = new MemberRequestDTO.CreateMember("ex2@gmail.com","cCeJvA99H7bV2ctvVIpM4Bh3ZJvawh3JnX3tREWGtNA="); + memberService.createMember(member2); + + MemberRequestDTO.CreateMember member3 = new MemberRequestDTO.CreateMember("ex3@gmail.com","cCeJvA99H7bV2ctvVIpM4Bh3ZJvawh3JnX3tREWGtNA="); + memberService.createMember(member3); } } diff --git a/src/main/java/team1/BE/seamless/mapper/MemberMapper.java b/src/main/java/team1/BE/seamless/mapper/MemberMapper.java index 56f6ce5..995cf84 100644 --- a/src/main/java/team1/BE/seamless/mapper/MemberMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/MemberMapper.java @@ -13,10 +13,10 @@ public class MemberMapper { public MemberEntity toEntity(CreateMember create, ProjectEntity project) { return new MemberEntity( - create.getName(), - create.getRole(), + "guest", + "", create.getEmail(), - create.getImageURL(), + "", project ); } diff --git a/src/main/java/team1/BE/seamless/repository/MemberRepository.java b/src/main/java/team1/BE/seamless/repository/MemberRepository.java index 0cac24b..d2ca7d1 100644 --- a/src/main/java/team1/BE/seamless/repository/MemberRepository.java +++ b/src/main/java/team1/BE/seamless/repository/MemberRepository.java @@ -17,4 +17,6 @@ public interface MemberRepository extends JpaRepository { Optional findByProjectEntityIdAndIdAndIsDeleteFalse(Long projectId, Long memberId); + Optional findByEmailAndIsDeleteFalse(String email); + } diff --git a/src/main/java/team1/BE/seamless/service/AttendURLService.java b/src/main/java/team1/BE/seamless/service/AttendURLService.java index 25744e8..44ff40e 100644 --- a/src/main/java/team1/BE/seamless/service/AttendURLService.java +++ b/src/main/java/team1/BE/seamless/service/AttendURLService.java @@ -50,7 +50,7 @@ public String generateAttendURL(HttpServletRequest req, @Valid Long projectId, @ // 코드는 프로젝트id + exp로 구성 // exp는 1일로 가정 String code = aesEncrypt.encrypt( - project.getId() + " " + LocalDateTime.now().plusDays(1).toString()); + project.getId() + " " + LocalDateTime.now().plusDays(1)); return DEFAULTURL + "invite?code=" + code; } diff --git a/src/main/java/team1/BE/seamless/service/AuthService.java b/src/main/java/team1/BE/seamless/service/AuthService.java index ac80bba..c304edb 100644 --- a/src/main/java/team1/BE/seamless/service/AuthService.java +++ b/src/main/java/team1/BE/seamless/service/AuthService.java @@ -1,6 +1,7 @@ package team1.BE.seamless.service; import jakarta.validation.Valid; +import java.time.LocalDateTime; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -90,7 +91,7 @@ protected UserEntity saveOrUpdate(OAuthAttributes attributes) { return userRepository.save(user); } - public Token memberCodeJoin(@Valid String memberCode) { + public Token memberCodeJoin(String memberCode) { // decode String code = aesEncrypt.decrypt(memberCode); @@ -104,9 +105,10 @@ public Token memberCodeJoin(@Valid String memberCode) { return new Token(token); } - public String memberCodeCreate(@Valid String memberCode) { + public String memberCodeCreate(String memberCode) { // ENCODE - String code = aesEncrypt.encrypt(memberCode); + String code = aesEncrypt.encrypt( + 1 + "_" + LocalDateTime.now().plusDays(1000)); return code; } } \ No newline at end of file diff --git a/src/main/java/team1/BE/seamless/service/MemberService.java b/src/main/java/team1/BE/seamless/service/MemberService.java index 4102347..70e63cd 100644 --- a/src/main/java/team1/BE/seamless/service/MemberService.java +++ b/src/main/java/team1/BE/seamless/service/MemberService.java @@ -17,6 +17,8 @@ import team1.BE.seamless.mapper.MemberMapper; import team1.BE.seamless.repository.MemberRepository; import team1.BE.seamless.repository.ProjectRepository; +import team1.BE.seamless.util.Util; +import team1.BE.seamless.util.auth.AesEncrypt; import team1.BE.seamless.util.auth.ParsingPram; import team1.BE.seamless.util.errorException.BaseHandler; @@ -29,14 +31,16 @@ public class MemberService { private final MemberMapper memberMapper; private final ProjectRepository projectRepository; private final ParsingPram parsingPram; + private final AesEncrypt aesEncrypt; @Autowired public MemberService(MemberRepository memberRepository, MemberMapper memberMapper, - ProjectRepository projectRepository, ParsingPram parsingPram) { + ProjectRepository projectRepository, ParsingPram parsingPram, AesEncrypt aesEncrypt) { this.memberRepository = memberRepository; this.memberMapper = memberMapper; this.projectRepository = projectRepository; this.parsingPram = parsingPram; + this.aesEncrypt = aesEncrypt; } public MemberResponseDTO getMember(Long projectId, Long memberId, HttpServletRequest req) { @@ -87,10 +91,24 @@ public MemberResponseDTO createMember(Long projectId, CreateMember create, HttpS return memberMapper.toCreateResponseDTO(member); } - public MemberResponseDTO createMember(Long projectId, CreateMember create) { - // 테스트용 오버로딩임. 삭제 금지 + @Transactional + public MemberResponseDTO createMember(CreateMember create) { - ProjectEntity project = projectRepository.findById(projectId) +// 프로젝트id, exp + String[] temp = aesEncrypt.decrypt(create.getCode()).split("_"); + +// exp검사 + if (Util.parseDate(temp[1]).isBefore(LocalDateTime.now())) { + throw new BaseHandler(HttpStatus.FORBIDDEN,"초대 코드가 만료되었습니다."); + } + +// 멤버 이메일 중복 여부 검사 + if(memberRepository.findByEmailAndIsDeleteFalse(create.getEmail()).isPresent()){ + throw new BaseHandler(HttpStatus.UNAUTHORIZED,"이메일이 중복 됩니다."); + }; + +// 프로젝트 조회 + ProjectEntity project = projectRepository.findById(Long.parseLong(temp[0])) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); // if (project.getEndDate().isBefore(LocalDateTime.now())) { @@ -100,6 +118,12 @@ public MemberResponseDTO createMember(Long projectId, CreateMember create) { MemberEntity member = memberMapper.toEntity(create, project); memberRepository.save(member); +// 코드 생성 + String code = aesEncrypt.encrypt(member.getId().toString()); + System.out.println(code); + +// 이메일로 코드 전달(추가 요망) + return memberMapper.toCreateResponseDTO(member); } From 3097383a73752c1e5ffcb7129f0f11cc7132d66e Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:52:35 +0900 Subject: [PATCH 35/63] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EC=88=98=EC=A0=95(?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/util/Util.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/team1/BE/seamless/util/Util.java b/src/main/java/team1/BE/seamless/util/Util.java index 94d8cfd..58bba7b 100644 --- a/src/main/java/team1/BE/seamless/util/Util.java +++ b/src/main/java/team1/BE/seamless/util/Util.java @@ -1,5 +1,8 @@ package team1.BE.seamless.util; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + public class Util { public Util() { @@ -8,4 +11,11 @@ public Util() { public static boolean isNull(String str) { return str == null || str.isBlank(); } + + public static LocalDateTime parseDate(String date) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS"); + LocalDateTime dateTime = LocalDateTime.parse(date, formatter); + + return dateTime; + } } From 43a82e3f2a9135a8f9c3b6b86b6c91f25cba3456 Mon Sep 17 00:00:00 2001 From: suno-boy Date: Wed, 16 Oct 2024 17:22:34 +0900 Subject: [PATCH 36/63] =?UTF-8?q?feat:=20=ED=99=98=EA=B2=BD=EB=B3=80?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B3=B4?= =?UTF-8?q?=EB=82=BC=20=EB=95=8C,=20=EB=AF=BC=EA=B0=90=ED=95=9C=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EA=B0=80=EB=A6=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6079bcb..13b1836 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -23,8 +23,8 @@ spring.profiles.include=oauth spring.mvc.pathmatch.matching-strategy=ant_path_matcher spring.mail.host=smtp.gmail.com spring.mail.port=587 -spring.mail.username=example@gmail.com -spring.mail.password=123 +spring.mail.username=${mail.username} +spring.mail.password=${mail.password} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true \ No newline at end of file From da2a278e9f10079a8a07117485f5ad6ad41dbce6 Mon Sep 17 00:00:00 2001 From: suno-boy Date: Wed, 16 Oct 2024 17:23:29 +0900 Subject: [PATCH 37/63] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=EC=9D=84=20=EC=B0=B8=EC=97=AC=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EA=B5=AC=EC=84=B1=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/DTO/InviteRequestDTO.java | 12 +++++++- .../InviteCodeByEmailController.java | 7 ++--- .../service/InviteCodeByEmailService.java | 28 +++++++++++++++++-- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java b/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java index 4022a67..f063c31 100644 --- a/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/InviteRequestDTO.java @@ -6,13 +6,23 @@ public class InviteRequestDTO { private Long projectId; private String email; + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } public InviteRequestDTO() { } - public InviteRequestDTO(Long projectId, String email) { + public InviteRequestDTO(Long projectId, String email, String name) { this.projectId = projectId; this.email = email; + this.name = name; } public Long getProjectId() { diff --git a/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java b/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java index c73c6da..6428f7e 100644 --- a/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java +++ b/src/main/java/team1/BE/seamless/controller/InviteCodeByEmailController.java @@ -27,11 +27,8 @@ public class InviteCodeByEmailController { @PostMapping("/invite") public SingleResult inviteMemberToProject(@RequestBody InviteRequestDTO inviteRequest) { try { - String message = - "You have been invited to join the project with ID: " + inviteRequest.getProjectId() - + "\nAnd Participation code: 참여코드 들어가야함"; - inviteService.sendProjectInvite(inviteRequest.getEmail(), message, inviteRequest.getProjectId()); - return new SingleResult<>("이메일로 프로젝트 초대코드 전송이 성공적으로 처리되었습니다."); + inviteService.sendProjectInvite(inviteRequest.getEmail(), inviteRequest.getProjectId()); + return new SingleResult<>("팀원의 이메일로 프로젝트 초대코드 전송이 성공적으로 처리되었습니다."); } catch (Exception e) { throw new BaseHandler(HttpStatus.BAD_REQUEST,"이메일로 프로젝트 초대코드 전송이 실패되었습니다. : " + e.getMessage()); } diff --git a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java index 0b5c5d1..10f2017 100644 --- a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java +++ b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java @@ -12,6 +12,8 @@ import team1.BE.seamless.util.errorException.BaseHandler; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; @Service public class InviteCodeByEmailService { @@ -25,7 +27,7 @@ public class InviteCodeByEmailService { this.projectRepository = projectRepository; } - public void sendProjectInvite(String email, String message, Long projectId) { + public void sendProjectInvite(String email, Long projectId) { // 프로젝트 존재 검증 ProjectEntity project = projectRepository.findById(projectId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); @@ -37,10 +39,30 @@ public void sendProjectInvite(String email, String message, Long projectId) { // 팀원인지 팀장인지 검증은 필요없음. + + // 참여코드 생성 (UUID 기반 + 현재 시간) + String participationCode = generateParticipationCode(); + + // 이메일 메시지 내용 생성 + String message = "안녕하세요,\n\n" + + "프로젝트 '" + project.getName() + "'에 초대되었습니다.\n" + + "참여 코드는 다음과 같습니다: " + participationCode + "\n\n" + + "프로젝트에 참여하려면 초대 코드를 사용하여 등록해주세요.\n\n" + + "감사합니다."; + + SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setTo(email); - mailMessage.setSubject("여기엔 무엇을 채우는거지?"); + mailMessage.setSubject("[프로젝트 초대] 프로젝트 '" + project.getName() + "'에 참여하세요!"); // 이메일 제목 설정임. mailMessage.setText(message); mailSender.send(mailMessage); } -} + + + private String generateParticipationCode() { + String uniqueId = UUID.randomUUID().toString().substring(0, 8); // 8자리 코드임 + String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")); + return uniqueId + "-" + timestamp; // 참여코드 예: 1234abcd-202410161530 와 같이 생성됨. + // 이렇게 시간을 기준으로 만들면, 이전 or 다른 팀장의 프로젝트의 참여코드와 겹칠 일이 없게됨. + } +} \ No newline at end of file From 65ef91428a1ed3edbd6ffbd0cceafb1441570869 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Wed, 16 Oct 2024 17:33:10 +0900 Subject: [PATCH 38/63] =?UTF-8?q?refactor:=20Task=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EB=A0=88=EC=9D=B4=EC=96=B4=EC=9D=98=20@Valid=20?= =?UTF-8?q?=ED=82=A4=EC=9B=8C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/service/TaskService.java | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index 5ca6828..253457c 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -54,7 +54,7 @@ public Page getTaskList(Long projectId, getList param) { return taskEntities.map(taskMapper::toDetail); } - public TaskDetail createTask(HttpServletRequest req, @Valid Long projectId, Create create) { + public TaskDetail createTask(HttpServletRequest req, Long projectId, Create create) { ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( projectId, parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); @@ -75,8 +75,32 @@ public TaskDetail createTask(HttpServletRequest req, @Valid Long projectId, Crea return taskMapper.toDetail(taskEntity); } + // 테스트용 오버로딩 + public TaskDetail createTask(Long projectId, Create create) { + + ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( + projectId, "user1@google.com") + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); + +// 태스크의 일정 검증 + if (project.getStartDate().isAfter(create.getStartDate()) || project.getEndDate() + .isBefore(create.getEndDate())) { + throw new BaseHandler(HttpStatus.FORBIDDEN, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); + } + + MemberEntity member = memberRepository.findById(create.getMemberId()) + .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 멤버")); + + TaskEntity taskEntity = taskMapper.toEntity(project, member, create); + + taskRepository.save(taskEntity); + + return taskMapper.toDetail(taskEntity); + + } + @Transactional - public TaskDetail updateTask(HttpServletRequest req, @Valid Long taskId, @Valid Update update) { + public TaskDetail updateTask(HttpServletRequest req, Long taskId, Update update) { TaskEntity task = taskRepository.findByIdAndIsDeletedFalse(taskId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 태스크")); From 9ab9ee09cbd3c6004f3f869067fae12f68d20a4f Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Wed, 16 Oct 2024 19:33:13 +0900 Subject: [PATCH 39/63] =?UTF-8?q?fix=20:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0981716..64e2f21 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -119,7 +119,7 @@ jobs: CURRENT_PID=$(pgrep -f $JAR_NAME) - if [ -z $CURRENT_PID ] + if [ -n $CURRENT_PID ] then sleep 1 else From 515654bd461f27408667d642fc77d697b38cc9a8 Mon Sep 17 00:00:00 2001 From: suno-boy Date: Wed, 16 Oct 2024 19:35:24 +0900 Subject: [PATCH 40/63] =?UTF-8?q?docs:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=B4=88=EB=8C=80=EC=BD=94=EB=93=9C=20=EB=A9=94=EC=8B=9C?= =?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 --- .../team1/BE/seamless/service/InviteCodeByEmailService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java index 10f2017..1c75a74 100644 --- a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java +++ b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java @@ -47,7 +47,7 @@ public void sendProjectInvite(String email, Long projectId) { String message = "안녕하세요,\n\n" + "프로젝트 '" + project.getName() + "'에 초대되었습니다.\n" + "참여 코드는 다음과 같습니다: " + participationCode + "\n\n" + - "프로젝트에 참여하려면 초대 코드를 사용하여 등록해주세요.\n\n" + + "프로젝트에 참여하려면 초대 코드를 사용하여 입장해주세요.\n\n" + "감사합니다."; From f6d9953251eecc791b20b24f3f4633d9323883d8 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Wed, 16 Oct 2024 22:16:58 +0900 Subject: [PATCH 41/63] =?UTF-8?q?feat:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=84=A4=EC=A0=95=EC=9C=BC=EB=A1=9C=20Tas?= =?UTF-8?q?k=20=EC=83=9D=EC=84=B1=20=EC=8B=A4=ED=8C=A8=EC=8B=9C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=BD=94=EB=93=9C=20FORBIDDEN=EC=97=90?= =?UTF-8?q?=EC=84=9C=20BAD=20REQUEST=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/TaskDTO.java | 4 ++-- .../team1/BE/seamless/service/TaskService.java | 6 +++--- .../team1/BE/seamless/service/TaskServiceTest.java | 14 ++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java index c010de9..b4651bb 100644 --- a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java @@ -31,7 +31,7 @@ public static class Create { public Create(String name, String remark, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { if (endDate.isBefore(startDate)) { - throw new BaseHandler(HttpStatus.FORBIDDEN, "종료시간은 시작시간보다 이전일 수 없습니다."); + throw new BaseHandler(HttpStatus.BAD_REQUEST, "종료시간은 시작시간보다 이전일 수 없습니다."); } this.name = name; this.remark = remark; @@ -79,7 +79,7 @@ public Update(String name, String remark, Integer progress, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { if (endDate.isBefore(startDate)) { - throw new BaseHandler(HttpStatus.FORBIDDEN, "종료시간은 시작시간보다 이전일 수 없습니다."); + throw new BaseHandler(HttpStatus.BAD_REQUEST, "종료시간은 시작시간보다 이전일 수 없습니다."); } this.name = name; this.remark = remark; diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index 253457c..5c74b4c 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -62,7 +62,7 @@ public TaskDetail createTask(HttpServletRequest req, Long projectId, Create crea // 태스크의 일정 검증 if (project.getStartDate().isAfter(create.getStartDate()) || project.getEndDate() .isBefore(create.getEndDate())) { - throw new BaseHandler(HttpStatus.FORBIDDEN, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); + throw new BaseHandler(HttpStatus.BAD_REQUEST, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } MemberEntity member = memberRepository.findById(create.getMemberId()) @@ -85,7 +85,7 @@ public TaskDetail createTask(Long projectId, Create create) { // 태스크의 일정 검증 if (project.getStartDate().isAfter(create.getStartDate()) || project.getEndDate() .isBefore(create.getEndDate())) { - throw new BaseHandler(HttpStatus.FORBIDDEN, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); + throw new BaseHandler(HttpStatus.BAD_REQUEST, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } MemberEntity member = memberRepository.findById(create.getMemberId()) @@ -107,7 +107,7 @@ public TaskDetail updateTask(HttpServletRequest req, Long taskId, Update update) // 태스크의 일정 검증 if (task.getProject().getStartDate().isAfter(update.getStartDate()) || task.getProject() .getEndDate().isBefore(update.getEndDate())) { - throw new BaseHandler(HttpStatus.FORBIDDEN, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); + throw new BaseHandler(HttpStatus.BAD_REQUEST, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } // 수정 권한이 있는지 검증 diff --git a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java index 46dfc3f..a038704 100644 --- a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java +++ b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java @@ -24,6 +24,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit.jupiter.SpringExtension; +import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.TaskDTO.Create; import team1.BE.seamless.entity.ProjectEntity; import team1.BE.seamless.repository.MemberRepository; @@ -42,6 +43,7 @@ class TaskServiceTest { private HttpHeaders headers = new HttpHeaders(); private TaskService taskService; + private ProjectService projectService; private ProjectRepository projectRepository; private UserRepository userRepository; private MemberRepository memberRepository; @@ -73,28 +75,24 @@ public void setUp() { @Test public void 태스크_시작_시간이_프로젝트_일정_범위보다_이를_경우_실패() { - Optional project = projectRepository.findByIdAndIsDeletedFalse(1L); - - Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2024, 10, 10, 0, 0), LocalDateTime.of(2026, 5, 3, 1, 0, 0)); + Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2001, 10, 10, 0, 0), LocalDateTime.of(2025, 5, 3, 1, 0, 0)); HttpEntity requestEntity = new HttpEntity(body, headers); ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1/task", POST, requestEntity, String.class); - assertThat(responseEntity.getStatusCode()).isEqualTo(FORBIDDEN); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); } @Test public void 태스크_마감_시간이_프로젝트_일정_범위보다_늦을_경우_실패() { - Optional project = projectRepository.findByIdAndIsDeletedFalse(1L); - - Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2023, 12, 1, 0, 0), LocalDateTime.of(2024, 5, 3, 1, 0, 0)); + Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2024, 12, 1, 0, 0), LocalDateTime.of(2100, 5, 3, 1, 0, 0)); HttpEntity requestEntity = new HttpEntity(body, headers); ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1/task", POST, requestEntity, String.class); - assertThat(responseEntity.getStatusCode()).isEqualTo(FORBIDDEN); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); } } \ No newline at end of file From 18807793922359b522d826b94b2b5ef929d059f1 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 15:52:19 +0900 Subject: [PATCH 42/63] =?UTF-8?q?test:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=82=AD=EC=A0=9C=EC=8B=9C=20Task=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=8B=A4=ED=8C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/service/TaskService.java | 4 ++++ .../BE/seamless/service/TaskServiceTest.java | 22 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index 5c74b4c..f33989c 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -45,6 +45,10 @@ public TaskDetail getTask(Long taskId) { TaskEntity taskEntity = taskRepository.findByIdAndIsDeletedFalse(taskId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 태스크")); + Long projectId = taskEntity.getProject().getId(); + + ProjectEntity project = projectRepository.findByIdAndIsDeletedFalse(projectId).orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); + return taskMapper.toDetail(taskEntity); } diff --git a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java index a038704..63dd217 100644 --- a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java +++ b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java @@ -2,9 +2,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.FORBIDDEN; +import static org.springframework.http.HttpStatus.NOT_FOUND; import jakarta.servlet.http.HttpServletRequest; import java.time.LocalDateTime; @@ -26,7 +28,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.TaskDTO.Create; +import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.entity.ProjectEntity; +import team1.BE.seamless.entity.TaskEntity; import team1.BE.seamless.repository.MemberRepository; import team1.BE.seamless.repository.ProjectRepository; import team1.BE.seamless.repository.UserRepository; @@ -49,12 +53,13 @@ class TaskServiceTest { private MemberRepository memberRepository; @Autowired - public TaskServiceTest(TestRestTemplate restTemplate, TaskService taskService, ProjectRepository projectRepository, UserRepository userRepository, MemberRepository memberRepository) { + public TaskServiceTest(TestRestTemplate restTemplate, TaskService taskService, ProjectRepository projectRepository, UserRepository userRepository, MemberRepository memberRepository, ProjectService projectService) { this.restTemplate = restTemplate; this.taskService = taskService; this.projectRepository = projectRepository; this.userRepository = userRepository; this.memberRepository = memberRepository; + this.projectService = projectService; } @BeforeEach @@ -95,4 +100,19 @@ public void setUp() { assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); } + @Test + public void 프로젝트_삭제시_태스크_조회_실패() { + // 프로젝트 삭제 + projectService.deleteProject(1L); + + Exception exception = assertThrows(RuntimeException.class, () -> { + taskService.getTask(1L); + }); + + HttpEntity requestEntity = new HttpEntity<>(headers); + + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/task/1", GET, requestEntity, String.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(NOT_FOUND); + } } \ No newline at end of file From cbd13fda73e3b17a1187f573b37629b59a31a758 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 15:53:23 +0900 Subject: [PATCH 43/63] =?UTF-8?q?feat:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Task=20Creator=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team1/BE/seamless/init/InitData.java | 5 +++- .../team1/BE/seamless/init/TaskCreator.java | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/main/java/team1/BE/seamless/init/TaskCreator.java diff --git a/src/main/java/team1/BE/seamless/init/InitData.java b/src/main/java/team1/BE/seamless/init/InitData.java index 81ae9bf..b8c0756 100644 --- a/src/main/java/team1/BE/seamless/init/InitData.java +++ b/src/main/java/team1/BE/seamless/init/InitData.java @@ -14,15 +14,17 @@ public class InitData { private final UserCreator userCreator; private final OptionCreator optionCreator; private final MemberCreator memberCreator; + private final TaskCreator taskCreator; @Autowired public InitData(TestCreator testCreator, ProjectCreator projectCreator, UserCreator userCreator, - OptionCreator optionCreator, MemberCreator memberCreator) { + OptionCreator optionCreator, MemberCreator memberCreator, TaskCreator taskCreator) { this.testCreator = testCreator; this.projectCreator = projectCreator; this.userCreator = userCreator; this.optionCreator = optionCreator; this.memberCreator = memberCreator; + this.taskCreator = taskCreator; } @PostConstruct @@ -33,5 +35,6 @@ public void init() { optionCreator.creator(); projectCreator.creator(); memberCreator.creator(); + taskCreator.creator(); } } diff --git a/src/main/java/team1/BE/seamless/init/TaskCreator.java b/src/main/java/team1/BE/seamless/init/TaskCreator.java new file mode 100644 index 0000000..8675198 --- /dev/null +++ b/src/main/java/team1/BE/seamless/init/TaskCreator.java @@ -0,0 +1,27 @@ +package team1.BE.seamless.init; + +import java.time.LocalDateTime; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import team1.BE.seamless.DTO.TaskDTO; +import team1.BE.seamless.service.TaskService; + +@Component +public class TaskCreator { + + private final TaskService taskService; + + @Autowired + public TaskCreator(TaskService taskService) { + this.taskService = taskService; + } + + public void creator() { + + TaskDTO.Create task1 = new TaskDTO.Create("태스크1", "첫번째 태스크입니다.", 1L, + LocalDateTime.of(2024, 10, 10, 0, 0), + LocalDateTime.of(2025, 9, 3, 0, 0)); + + taskService.createTask(1L, task1); + } +} From 791420b719a96fd55bbe8c6809e517e3a422a366 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 15:55:23 +0900 Subject: [PATCH 44/63] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=95=84=EC=9D=B4=EB=94=94=EB=A1=9C=20Task=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=EC=A1=B4=EC=9E=AC=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/service/TaskService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index f33989c..ab6c44f 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -53,6 +53,8 @@ public TaskDetail getTask(Long taskId) { } public Page getTaskList(Long projectId, getList param) { + ProjectEntity project = projectRepository.findByIdAndIsDeletedFalse(projectId).orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); + Page taskEntities = taskRepository.findAllByProjectEntityIdAndIsDeletedFalse(projectId, param.toPageable()); return taskEntities.map(taskMapper::toDetail); From 90d783fb4496841e42ad7389b46d9a3591f9c295 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 15:58:35 +0900 Subject: [PATCH 45/63] =?UTF-8?q?test:=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=20=EC=A0=95=EC=83=81=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team1/BE/seamless/service/TaskServiceTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java index 63dd217..f46eb07 100644 --- a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java +++ b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java @@ -109,10 +109,16 @@ public void setUp() { taskService.getTask(1L); }); + String expectedMessage = "존재하지 않는 프로젝트"; + String actualMessage = exception.getMessage(); + + assertTrue(actualMessage.contains(expectedMessage)); + HttpEntity requestEntity = new HttpEntity<>(headers); ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/task/1", GET, requestEntity, String.class); assertThat(responseEntity.getStatusCode()).isEqualTo(NOT_FOUND); } + } \ No newline at end of file From e7a854346f22ba0a736c7abda754fe319ec0a959 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 21:18:28 +0900 Subject: [PATCH 46/63] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/controller/TaskController.java | 1 - src/main/java/team1/BE/seamless/service/TaskService.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/team1/BE/seamless/controller/TaskController.java b/src/main/java/team1/BE/seamless/controller/TaskController.java index bd80f71..8d010af 100644 --- a/src/main/java/team1/BE/seamless/controller/TaskController.java +++ b/src/main/java/team1/BE/seamless/controller/TaskController.java @@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.TaskDTO; import team1.BE.seamless.DTO.TaskDTO.TaskDetail; -import team1.BE.seamless.entity.TaskEntity; import team1.BE.seamless.service.TaskService; import team1.BE.seamless.util.page.PageMapper; import team1.BE.seamless.util.page.PageResult; diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index ab6c44f..ab7a0cc 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -1,7 +1,6 @@ package team1.BE.seamless.service; import jakarta.servlet.http.HttpServletRequest; -import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; From 9765231a505f9d5da9978dcece69d26594e5a3d8 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 21:20:49 +0900 Subject: [PATCH 47/63] =?UTF-8?q?fix:=20@Pattern=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/ProjectDTO.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java b/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java index 179eb4c..e9610d3 100644 --- a/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java @@ -32,11 +32,11 @@ public static class ProjectCreate { private List<@Positive Long> optionIds = new ArrayList<>(); @NotNull - @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") +// @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime startDate; @NotNull - @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") +// @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime endDate; public ProjectCreate() { From d8b6247c8a54c536b097c9536b29eccba9bb1fc2 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Thu, 17 Oct 2024 21:21:05 +0900 Subject: [PATCH 48/63] =?UTF-8?q?fix=20:=20=EC=A0=95=EA=B7=9C=ED=91=9C?= =?UTF-8?q?=ED=98=84=EC=8B=9D=EC=9D=84=20=ED=86=B5=ED=95=9C=20localDateTim?= =?UTF-8?q?e=20validation=20=EC=98=A4=EB=A5=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/ProjectDTO.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java b/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java index 179eb4c..f58cc23 100644 --- a/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/ProjectDTO.java @@ -32,11 +32,11 @@ public static class ProjectCreate { private List<@Positive Long> optionIds = new ArrayList<>(); @NotNull - @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") +// @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime startDate; @NotNull - @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") +// @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime endDate; public ProjectCreate() { @@ -90,10 +90,10 @@ public static class ProjectUpdate { @Valid private List<@Positive Long> optionIds = new ArrayList<>(); - @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") +// @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime startDate; - @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") +// @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$") private LocalDateTime endDate; public ProjectUpdate() { From 5a130729e0d5949f6bacedfcd6ca3096816b0cb7 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Thu, 17 Oct 2024 21:21:39 +0900 Subject: [PATCH 49/63] =?UTF-8?q?fix=20:=20detail=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EC=8B=9C=20ProjectOption=20Id=EB=A5=BC=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8D=98=20=EA=B2=83=EC=9D=84=20Option=20Id=EB=A5=BC?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=ED=95=98=EA=B2=8C=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/mapper/ProjectMapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java b/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java index ccc75aa..875b220 100644 --- a/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/ProjectMapper.java @@ -6,6 +6,7 @@ import team1.BE.seamless.DTO.ProjectDTO; import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.ProjectDTO.ProjectPeriod; +import team1.BE.seamless.entity.OptionEntity; import team1.BE.seamless.entity.ProjectEntity; import team1.BE.seamless.entity.ProjectOption; import team1.BE.seamless.entity.UserEntity; @@ -30,7 +31,7 @@ public ProjectDetail toDetail(ProjectEntity projectEntity) { projectEntity.getName(), projectEntity.getStartDate(), projectEntity.getEndDate(), - projectEntity.getProjectOptions().stream().map(ProjectOption::getId).toList() + projectEntity.getProjectOptions().stream().map(ProjectOption::getOptionEntity).map(OptionEntity::getId).toList() ); } From 16f2fb236059d21b993e18434bec5c45507ec97e Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 21:26:29 +0900 Subject: [PATCH 50/63] =?UTF-8?q?refactor:=20Create=EB=A5=BC=20TaskCreate?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/team1/BE/seamless/DTO/TaskDTO.java | 4 ++-- .../seamless/controller/TaskController.java | 4 ++-- .../team1/BE/seamless/init/TaskCreator.java | 4 ++-- .../team1/BE/seamless/mapper/TaskMapper.java | 13 +++++------ .../BE/seamless/service/TaskService.java | 22 +++++++++---------- .../BE/seamless/service/TaskServiceTest.java | 16 +++----------- 6 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java index b4651bb..cc0a320 100644 --- a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java @@ -13,7 +13,7 @@ public static class getList extends PageParam { } - public static class Create { + public static class TaskCreate { @NotBlank(message = "이름은 필수 입력 사항입니다.") private String name; @@ -28,7 +28,7 @@ public static class Create { @NotNull(message = "종료 시간은 필수 입력 사항입니다.") private LocalDateTime endDate; - public Create(String name, String remark, Long memberId, LocalDateTime startDate, + public TaskCreate(String name, String remark, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { if (endDate.isBefore(startDate)) { throw new BaseHandler(HttpStatus.BAD_REQUEST, "종료시간은 시작시간보다 이전일 수 없습니다."); diff --git a/src/main/java/team1/BE/seamless/controller/TaskController.java b/src/main/java/team1/BE/seamless/controller/TaskController.java index 8d010af..56c3f31 100644 --- a/src/main/java/team1/BE/seamless/controller/TaskController.java +++ b/src/main/java/team1/BE/seamless/controller/TaskController.java @@ -51,8 +51,8 @@ public PageResult getTaskList(@PathVariable Long projectId, @Operation(summary = "태스크 생성") @PostMapping("/{projectId}/task") public SingleResult createTask(HttpServletRequest req, - @Valid @PathVariable Long projectId, @Valid @RequestBody TaskDTO.Create create) { - return new SingleResult<>(taskService.createTask(req, projectId, create)); + @Valid @PathVariable Long projectId, @Valid @RequestBody TaskDTO.TaskCreate taskCreate) { + return new SingleResult<>(taskService.createTask(req, projectId, taskCreate)); } /** diff --git a/src/main/java/team1/BE/seamless/init/TaskCreator.java b/src/main/java/team1/BE/seamless/init/TaskCreator.java index 8675198..2ab369a 100644 --- a/src/main/java/team1/BE/seamless/init/TaskCreator.java +++ b/src/main/java/team1/BE/seamless/init/TaskCreator.java @@ -3,7 +3,7 @@ import java.time.LocalDateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import team1.BE.seamless.DTO.TaskDTO; +import team1.BE.seamless.DTO.TaskDTO.TaskCreate; import team1.BE.seamless.service.TaskService; @Component @@ -18,7 +18,7 @@ public TaskCreator(TaskService taskService) { public void creator() { - TaskDTO.Create task1 = new TaskDTO.Create("태스크1", "첫번째 태스크입니다.", 1L, + TaskCreate task1 = new TaskCreate("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2024, 10, 10, 0, 0), LocalDateTime.of(2025, 9, 3, 0, 0)); diff --git a/src/main/java/team1/BE/seamless/mapper/TaskMapper.java b/src/main/java/team1/BE/seamless/mapper/TaskMapper.java index 51ab0bf..c3a5b6a 100644 --- a/src/main/java/team1/BE/seamless/mapper/TaskMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/TaskMapper.java @@ -1,8 +1,7 @@ package team1.BE.seamless.mapper; -import java.time.LocalDateTime; import org.springframework.stereotype.Component; -import team1.BE.seamless.DTO.TaskDTO.Create; +import team1.BE.seamless.DTO.TaskDTO.TaskCreate; import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.DTO.TaskDTO.Update; import team1.BE.seamless.entity.MemberEntity; @@ -13,14 +12,14 @@ @Component public class TaskMapper { - public TaskEntity toEntity(ProjectEntity project, MemberEntity member, Create create) { + public TaskEntity toEntity(ProjectEntity project, MemberEntity member, TaskCreate taskCreate) { return new TaskEntity( - create.getName(), - create.getRemark(), + taskCreate.getName(), + taskCreate.getRemark(), project, member, - create.getStartDate(), - create.getEndDate()); + taskCreate.getStartDate(), + taskCreate.getEndDate()); } public TaskEntity toUpdate(TaskEntity task, Update update) { diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index ab7a0cc..c28b131 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -6,7 +6,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import team1.BE.seamless.DTO.TaskDTO.Create; +import team1.BE.seamless.DTO.TaskDTO.TaskCreate; import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.DTO.TaskDTO.Update; import team1.BE.seamless.DTO.TaskDTO.getList; @@ -59,21 +59,21 @@ public Page getTaskList(Long projectId, getList param) { return taskEntities.map(taskMapper::toDetail); } - public TaskDetail createTask(HttpServletRequest req, Long projectId, Create create) { + public TaskDetail createTask(HttpServletRequest req, Long projectId, TaskCreate taskCreate) { ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( projectId, parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); // 태스크의 일정 검증 - if (project.getStartDate().isAfter(create.getStartDate()) || project.getEndDate() - .isBefore(create.getEndDate())) { + if (project.getStartDate().isAfter(taskCreate.getStartDate()) || project.getEndDate() + .isBefore(taskCreate.getEndDate())) { throw new BaseHandler(HttpStatus.BAD_REQUEST, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } - MemberEntity member = memberRepository.findById(create.getMemberId()) + MemberEntity member = memberRepository.findById(taskCreate.getMemberId()) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 멤버")); - TaskEntity taskEntity = taskMapper.toEntity(project, member, create); + TaskEntity taskEntity = taskMapper.toEntity(project, member, taskCreate); taskRepository.save(taskEntity); @@ -81,22 +81,22 @@ public TaskDetail createTask(HttpServletRequest req, Long projectId, Create crea } // 테스트용 오버로딩 - public TaskDetail createTask(Long projectId, Create create) { + public TaskDetail createTask(Long projectId, TaskCreate taskCreate) { ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( projectId, "user1@google.com") .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); // 태스크의 일정 검증 - if (project.getStartDate().isAfter(create.getStartDate()) || project.getEndDate() - .isBefore(create.getEndDate())) { + if (project.getStartDate().isAfter(taskCreate.getStartDate()) || project.getEndDate() + .isBefore(taskCreate.getEndDate())) { throw new BaseHandler(HttpStatus.BAD_REQUEST, "태스크는 프로젝트의 기한을 넘어설 수 없습니다."); } - MemberEntity member = memberRepository.findById(create.getMemberId()) + MemberEntity member = memberRepository.findById(taskCreate.getMemberId()) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 멤버")); - TaskEntity taskEntity = taskMapper.toEntity(project, member, create); + TaskEntity taskEntity = taskMapper.toEntity(project, member, taskCreate); taskRepository.save(taskEntity); diff --git a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java index f46eb07..7b9c44d 100644 --- a/src/test/java/team1/BE/seamless/service/TaskServiceTest.java +++ b/src/test/java/team1/BE/seamless/service/TaskServiceTest.java @@ -5,20 +5,14 @@ import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; -import jakarta.servlet.http.HttpServletRequest; import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpEntity; @@ -26,11 +20,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit.jupiter.SpringExtension; -import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; -import team1.BE.seamless.DTO.TaskDTO.Create; -import team1.BE.seamless.DTO.TaskDTO.TaskDetail; -import team1.BE.seamless.entity.ProjectEntity; -import team1.BE.seamless.entity.TaskEntity; +import team1.BE.seamless.DTO.TaskDTO.TaskCreate; import team1.BE.seamless.repository.MemberRepository; import team1.BE.seamless.repository.ProjectRepository; import team1.BE.seamless.repository.UserRepository; @@ -80,7 +70,7 @@ public void setUp() { @Test public void 태스크_시작_시간이_프로젝트_일정_범위보다_이를_경우_실패() { - Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2001, 10, 10, 0, 0), LocalDateTime.of(2025, 5, 3, 1, 0, 0)); + TaskCreate body = new TaskCreate("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2001, 10, 10, 0, 0), LocalDateTime.of(2025, 5, 3, 1, 0, 0)); HttpEntity requestEntity = new HttpEntity(body, headers); @@ -91,7 +81,7 @@ public void setUp() { @Test public void 태스크_마감_시간이_프로젝트_일정_범위보다_늦을_경우_실패() { - Create body = new Create("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2024, 12, 1, 0, 0), LocalDateTime.of(2100, 5, 3, 1, 0, 0)); + TaskCreate body = new TaskCreate("태스크1", "첫번째 태스크입니다.", 1L, LocalDateTime.of(2024, 12, 1, 0, 0), LocalDateTime.of(2100, 5, 3, 1, 0, 0)); HttpEntity requestEntity = new HttpEntity(body, headers); From 554dd3c5505438b4e20374df3df3fbfa21576236 Mon Sep 17 00:00:00 2001 From: seoyoung-dev Date: Thu, 17 Oct 2024 21:36:04 +0900 Subject: [PATCH 51/63] =?UTF-8?q?refactor:=20Update=EB=A5=BC=20TaskUpdate?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/TaskDTO.java | 4 ++-- .../java/team1/BE/seamless/controller/TaskController.java | 6 ++++-- src/main/java/team1/BE/seamless/mapper/TaskMapper.java | 4 ++-- src/main/java/team1/BE/seamless/service/TaskService.java | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java index cc0a320..1ab3b67 100644 --- a/src/main/java/team1/BE/seamless/DTO/TaskDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/TaskDTO.java @@ -61,7 +61,7 @@ public Long getMemberId() { } } - public static class Update { + public static class TaskUpdate { private String name; @@ -75,7 +75,7 @@ public static class Update { private LocalDateTime endDate; - public Update(String name, String remark, Integer progress, Long memberId, + public TaskUpdate(String name, String remark, Integer progress, Long memberId, LocalDateTime startDate, LocalDateTime endDate) { if (endDate.isBefore(startDate)) { diff --git a/src/main/java/team1/BE/seamless/controller/TaskController.java b/src/main/java/team1/BE/seamless/controller/TaskController.java index 56c3f31..8ea4309 100644 --- a/src/main/java/team1/BE/seamless/controller/TaskController.java +++ b/src/main/java/team1/BE/seamless/controller/TaskController.java @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import team1.BE.seamless.DTO.TaskDTO; +import team1.BE.seamless.DTO.TaskDTO.TaskCreate; +import team1.BE.seamless.DTO.TaskDTO.TaskUpdate; import team1.BE.seamless.DTO.TaskDTO.TaskDetail; import team1.BE.seamless.service.TaskService; import team1.BE.seamless.util.page.PageMapper; @@ -51,7 +53,7 @@ public PageResult getTaskList(@PathVariable Long projectId, @Operation(summary = "태스크 생성") @PostMapping("/{projectId}/task") public SingleResult createTask(HttpServletRequest req, - @Valid @PathVariable Long projectId, @Valid @RequestBody TaskDTO.TaskCreate taskCreate) { + @Valid @PathVariable Long projectId, @Valid @RequestBody TaskCreate taskCreate) { return new SingleResult<>(taskService.createTask(req, projectId, taskCreate)); } @@ -62,7 +64,7 @@ public SingleResult createTask(HttpServletRequest req, @PutMapping("/task/{taskId}") public SingleResult updateTask(HttpServletRequest req, @Valid @PathVariable Long taskId, - @Valid @RequestBody TaskDTO.Update update) { + @Valid @RequestBody TaskUpdate update) { return new SingleResult<>(taskService.updateTask(req, taskId, update)); } diff --git a/src/main/java/team1/BE/seamless/mapper/TaskMapper.java b/src/main/java/team1/BE/seamless/mapper/TaskMapper.java index c3a5b6a..c79398a 100644 --- a/src/main/java/team1/BE/seamless/mapper/TaskMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/TaskMapper.java @@ -3,7 +3,7 @@ import org.springframework.stereotype.Component; import team1.BE.seamless.DTO.TaskDTO.TaskCreate; import team1.BE.seamless.DTO.TaskDTO.TaskDetail; -import team1.BE.seamless.DTO.TaskDTO.Update; +import team1.BE.seamless.DTO.TaskDTO.TaskUpdate; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; import team1.BE.seamless.entity.TaskEntity; @@ -22,7 +22,7 @@ public TaskEntity toEntity(ProjectEntity project, MemberEntity member, TaskCreat taskCreate.getEndDate()); } - public TaskEntity toUpdate(TaskEntity task, Update update) { + public TaskEntity toUpdate(TaskEntity task, TaskUpdate update) { task.setName(Util.isNull(update.getName()) ? task.getName() : update.getName()); task.setRemark(Util.isNull(update.getRemark()) ? task.getRemark() : update.getRemark()); task.setProgress(Util.isNull(update.getProgress().toString()) ? task.getProgress() diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index c28b131..2d95939 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -8,7 +8,7 @@ import org.springframework.transaction.annotation.Transactional; import team1.BE.seamless.DTO.TaskDTO.TaskCreate; import team1.BE.seamless.DTO.TaskDTO.TaskDetail; -import team1.BE.seamless.DTO.TaskDTO.Update; +import team1.BE.seamless.DTO.TaskDTO.TaskUpdate; import team1.BE.seamless.DTO.TaskDTO.getList; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; @@ -105,7 +105,7 @@ public TaskDetail createTask(Long projectId, TaskCreate taskCreate) { } @Transactional - public TaskDetail updateTask(HttpServletRequest req, Long taskId, Update update) { + public TaskDetail updateTask(HttpServletRequest req, Long taskId, TaskUpdate update) { TaskEntity task = taskRepository.findByIdAndIsDeletedFalse(taskId) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 태스크")); From e12d9d0ef9457864385db11388097faa4c507048 Mon Sep 17 00:00:00 2001 From: suno-boy Date: Fri, 18 Oct 2024 14:48:01 +0900 Subject: [PATCH 52/63] =?UTF-8?q?docs:=20=EC=A3=BC=EC=84=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20=ED=95=84=EC=9A=94=20=EC=97=86=EB=8A=94?= =?UTF-8?q?=20DTO=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seamless/DTO/MemberDetailResponseDTO.java | 73 ------------------- .../service/InviteCodeByEmailService.java | 2 +- 2 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 src/main/java/team1/BE/seamless/DTO/MemberDetailResponseDTO.java diff --git a/src/main/java/team1/BE/seamless/DTO/MemberDetailResponseDTO.java b/src/main/java/team1/BE/seamless/DTO/MemberDetailResponseDTO.java deleted file mode 100644 index 73ef5ce..0000000 --- a/src/main/java/team1/BE/seamless/DTO/MemberDetailResponseDTO.java +++ /dev/null @@ -1,73 +0,0 @@ -package team1.BE.seamless.DTO; - -import java.util.List; -import team1.BE.seamless.entity.ProjectEntity; -import team1.BE.seamless.entity.TaskEntity; - -public class MemberDetailResponseDTO { - - private String email; - private String role; - private String name; - private String imageURL; - private ProjectEntity projectEntity; - private List taskEntities; - - public MemberDetailResponseDTO(String email, String role, String name, - String imageURL, ProjectEntity projectEntity, List taskEntities) { - this.email = email; - this.role = role; - this.name = name; - this.imageURL = imageURL; - this.projectEntity = projectEntity; - this.taskEntities = taskEntities; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getRole() { - return role; - } - - public void setRole(String role) { - this.role = role; - } - - public String getImageURL() { - return imageURL; - } - - public void setImageURL(String imageURL) { - this.imageURL = imageURL; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public ProjectEntity getProjectEntity() { - return projectEntity; - } - - public void setProjectEntity(ProjectEntity projectEntity) { - this.projectEntity = projectEntity; - } - - public List getTaskEntities() { - return taskEntities; - } - - public void setTaskEntities(List taskEntities) { - this.taskEntities = taskEntities; - } -} diff --git a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java index 1c75a74..b8a57f0 100644 --- a/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java +++ b/src/main/java/team1/BE/seamless/service/InviteCodeByEmailService.java @@ -37,7 +37,7 @@ public void sendProjectInvite(String email, Long projectId) { // throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); // } // 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ - // 팀원인지 팀장인지 검증은 필요없음. + // 팀원인지 팀장인지 검증은 필요없음.(어차피 이 post요청은 아무 권한 없는 사람이 보내는 것 취급임) ㄴ // 참여코드 생성 (UUID 기반 + 현재 시간) From e694f49dff9d251218cf3faf547c599e9eda71ea Mon Sep 17 00:00:00 2001 From: suno-boy Date: Fri, 18 Oct 2024 15:23:06 +0900 Subject: [PATCH 53/63] =?UTF-8?q?test:=20=EB=A9=A4=EB=B2=84=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 멤버 이메일 빈 칸으로 정보 수정시 반영이 안되거나 예외처리 되는가? - 멤버 삭제(softdelete로 되는가, 다시 조회하면 조회 되는가) --- .../seamless/service/MemberServiceTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/java/team1/BE/seamless/service/MemberServiceTest.java diff --git a/src/test/java/team1/BE/seamless/service/MemberServiceTest.java b/src/test/java/team1/BE/seamless/service/MemberServiceTest.java new file mode 100644 index 0000000..b9b1fb7 --- /dev/null +++ b/src/test/java/team1/BE/seamless/service/MemberServiceTest.java @@ -0,0 +1,94 @@ +package team1.BE.seamless.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpMethod.GET; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.OK; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; + +import team1.BE.seamless.DTO.MemberRequestDTO.UpdateMember; +import team1.BE.seamless.entity.MemberEntity; +import team1.BE.seamless.service.MemberService; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class MemberServiceTest { + + @LocalServerPort + private int port; + private String url = "http://localhost:"; + private final TestRestTemplate restTemplate; + private String token; + private HttpHeaders headers = new HttpHeaders(); + private final MemberService memberService; + + @Autowired + public MemberServiceTest(TestRestTemplate restTemplate, MemberService memberService) { + this.restTemplate = restTemplate; + this.memberService = memberService; + } + + @BeforeEach + public void setUp() { + HttpEntity requestEntity = new HttpEntity<>(null); + ResponseEntity responseEntity = restTemplate.exchange( + url + port + "/api/test/userToken/1", + PUT, + requestEntity, String.class); + + int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + int endIndex = responseEntity.getBody().indexOf("\"", startIndex); + token = responseEntity.getBody().substring(startIndex, endIndex); + + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(token); + this.url += port + "/api/project/1/member/"; + } + + + //"멤버 이메일 빈 칸으로 정보 수정시 반영이 안되거나 예외처리 되는가?" 에 대한 테스트 + @Test + void updateMemberWithEmptyEmailShouldFail() { + UpdateMember updateInfo = new UpdateMember("새로운 이름", "팀원", "", "http://example.com/"); + HttpEntity requestEntity = new HttpEntity<>(updateInfo, headers); + + ResponseEntity response = restTemplate.exchange( + url + "1", // 테스트 Member ID + PUT, + requestEntity, + String.class); + + assertThat(response.getStatusCode()).isEqualTo(BAD_REQUEST); + } + + + + // "멤버 삭제(softdelete로 되는가, 다시 조회하면 조회 되는가)" 에 대한 테스트 + @Test + void softDeleteMemberAndRequery() { + // Soft delete member + restTemplate.exchange(url + "1", // 테스트 Member ID + PUT, + new HttpEntity<>(new UpdateMember("", "팀원", "email@example.com", "http://example.com", true), headers), + Void.class); + + ResponseEntity response = restTemplate.exchange( + url + "123", + GET, + new HttpEntity<>(headers), + MemberEntity.class); + + assertThat(response.getStatusCode()).isEqualTo(NOT_FOUND); + } +} From 014d68849f9e69d45dd503f929f5938074671318 Mon Sep 17 00:00:00 2001 From: suno-boy Date: Fri, 18 Oct 2024 15:24:02 +0900 Subject: [PATCH 54/63] =?UTF-8?q?refactor:=20test=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20MemberRequestDTO=EC=97=90=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java b/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java index 5c58e4b..59e8cb7 100644 --- a/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/MemberRequestDTO.java @@ -70,6 +70,14 @@ public static class UpdateMember { public UpdateMember() { } + public UpdateMember(String name, String role, String email, String imageURL, boolean test) { + this.name = name; + this.role = role; + this.email = email; + this.imageURL = imageURL; + + } + public UpdateMember(String name, String role, String email, String imageURL) { this.name = name; this.role = role; From fda519a294a65ce738f4bb4ccc30f6e53e05ce86 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Fri, 18 Oct 2024 15:28:35 +0900 Subject: [PATCH 55/63] =?UTF-8?q?fix=20:=20=ED=95=B4=EB=8B=B9=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=A0=9D=ED=8A=B8=EC=97=90=20=EC=86=8D=ED=95=9C=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20DTO=EB=A5=BC?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B0=94?= =?UTF-8?q?=EA=BF=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/controller/ProjectController.java | 3 ++- .../team1/BE/seamless/service/ProjectService.java | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/team1/BE/seamless/controller/ProjectController.java b/src/main/java/team1/BE/seamless/controller/ProjectController.java index 214124b..ece1b2e 100644 --- a/src/main/java/team1/BE/seamless/controller/ProjectController.java +++ b/src/main/java/team1/BE/seamless/controller/ProjectController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import team1.BE.seamless.DTO.MemberResponseDTO; import team1.BE.seamless.DTO.ProjectDTO; import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.ProjectDTO.ProjectPeriod; @@ -62,7 +63,7 @@ public PageResult getProjectPeriod(@Valid ProjectDTO.getList para @Operation(summary = "프로젝트 멤버 조회") @GetMapping("/{project-id}/members") - public ListResult getProjectMembers(@Valid @PathVariable("project-id") Long id) { + public ListResult getProjectMembers(@Valid @PathVariable("project-id") Long id) { return new ListResult<>(projectService.getProjectMembers(id)); } diff --git a/src/main/java/team1/BE/seamless/service/ProjectService.java b/src/main/java/team1/BE/seamless/service/ProjectService.java index 3c2d468..6e2cc62 100644 --- a/src/main/java/team1/BE/seamless/service/ProjectService.java +++ b/src/main/java/team1/BE/seamless/service/ProjectService.java @@ -6,16 +6,17 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import team1.BE.seamless.DTO.MemberResponseDTO; import team1.BE.seamless.DTO.ProjectDTO; import team1.BE.seamless.DTO.ProjectDTO.ProjectCreate; import team1.BE.seamless.DTO.ProjectDTO.ProjectDetail; import team1.BE.seamless.DTO.ProjectDTO.ProjectPeriod; import team1.BE.seamless.DTO.ProjectDTO.ProjectUpdate; -import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.OptionEntity; import team1.BE.seamless.entity.ProjectEntity; import team1.BE.seamless.entity.ProjectOption; import team1.BE.seamless.entity.UserEntity; +import team1.BE.seamless.mapper.MemberMapper; import team1.BE.seamless.mapper.ProjectMapper; import team1.BE.seamless.repository.OptionRepository; import team1.BE.seamless.repository.ProjectRepository; @@ -29,14 +30,16 @@ public class ProjectService { private final UserRepository userRepository; private final OptionRepository optionRepository; private final ProjectMapper projectMapper; + private final MemberMapper memberMapper; @Autowired public ProjectService(ProjectRepository projectRepository, UserRepository userRepository, - OptionRepository optionRepository, ProjectMapper projectMapper) { + OptionRepository optionRepository, ProjectMapper projectMapper, MemberMapper memberMapper) { this.projectRepository = projectRepository; this.userRepository = userRepository; this.optionRepository = optionRepository; this.projectMapper = projectMapper; + this.memberMapper = memberMapper; } /** @@ -65,10 +68,10 @@ public ProjectDetail getProject(long id) { * @param id : 프로젝트 Id * @return : 해당 id를 가진 프로젝트에 참여한 팀원들의 목록 * */ - public List getProjectMembers(long id) { + public List getProjectMembers(long id) { ProjectEntity projectEntity = projectRepository.findById(id) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); - return projectEntity.getMemberEntities(); + return projectEntity.getMemberEntities().stream().map( entity -> memberMapper.toGetResponseDTO(entity)).toList(); } /** From 6610f8ecd733ed663c350e28cc3de512a7c1e77e Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Fri, 18 Oct 2024 15:44:47 +0900 Subject: [PATCH 56/63] =?UTF-8?q?fix=20:=20=EC=9D=B4=EB=AF=B8=20=EC=A7=80?= =?UTF-8?q?=EC=9B=8C=EC=A7=84=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20BAD=5FREQUEST?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/service/ProjectService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/team1/BE/seamless/service/ProjectService.java b/src/main/java/team1/BE/seamless/service/ProjectService.java index 6e2cc62..38f963b 100644 --- a/src/main/java/team1/BE/seamless/service/ProjectService.java +++ b/src/main/java/team1/BE/seamless/service/ProjectService.java @@ -157,6 +157,9 @@ public Long deleteProject(long id) { ProjectEntity projectEntity = projectRepository.findById(id) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "프로젝트가 존재하지 않음")); + if(projectEntity.getIsDeleted()) { + throw new BaseHandler(HttpStatus.BAD_REQUEST, "해당 프로젝트는 지워진 상태 입니다."); + } projectEntity.setIsDeleted(true); return projectEntity.getId(); } From 711b09d510b1e91008cab5d3d281d68d7623c68b Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:54:44 +0900 Subject: [PATCH 57/63] =?UTF-8?q?test:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1(=EC=9E=91=EC=84=B1=EC=A4=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seamless/service/ProjectServiceTest.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/test/java/team1/BE/seamless/service/ProjectServiceTest.java diff --git a/src/test/java/team1/BE/seamless/service/ProjectServiceTest.java b/src/test/java/team1/BE/seamless/service/ProjectServiceTest.java new file mode 100644 index 0000000..1a73275 --- /dev/null +++ b/src/test/java/team1/BE/seamless/service/ProjectServiceTest.java @@ -0,0 +1,58 @@ +package team1.BE.seamless.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import team1.BE.seamless.DTO.UserDTO.UserUpdate; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class ProjectServiceTest { + + @LocalServerPort + private int port; + private String url = "http://localhost:"; + private final TestRestTemplate restTemplate; + private String token; + private HttpHeaders headers = new HttpHeaders(); + + private final ProjectService projectService; + + + @Autowired + public ProjectServiceTest(TestRestTemplate restTemplate, ProjectService projectService) { + this.restTemplate = restTemplate; + this.projectService = projectService; + } + + /** + * 특정 유저id의 토큰 파싱 + */ + @BeforeEach + public void setUp() { + HttpEntity requestEntity = new HttpEntity<>(null); + ResponseEntity responseEntity = restTemplate.exchange( + url + port + "/api/test/userToken/1", + POST, + requestEntity, String.class); + + int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + int endIndex = responseEntity.getBody().indexOf("\"", startIndex); + + token = responseEntity.getBody().substring(startIndex, endIndex); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(token); + } +} \ No newline at end of file From 6492bc98969123687ca5771a6c8865e613257292 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:07:31 +0900 Subject: [PATCH 58/63] =?UTF-8?q?docs:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/util/auth/CookieUtils.java | 57 +++++++++++++++++++ ...eOAuth2AuthorizationRequestRepository.java | 50 ++++++++++++++++ .../BE/seamless/util/auth/SecurityConfig.java | 14 ++++- 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 src/main/java/team1/BE/seamless/util/auth/CookieUtils.java create mode 100644 src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java diff --git a/src/main/java/team1/BE/seamless/util/auth/CookieUtils.java b/src/main/java/team1/BE/seamless/util/auth/CookieUtils.java new file mode 100644 index 0000000..3f4ca7b --- /dev/null +++ b/src/main/java/team1/BE/seamless/util/auth/CookieUtils.java @@ -0,0 +1,57 @@ +package team1.BE.seamless.util.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Base64; +import java.util.Optional; +import org.springframework.util.SerializationUtils; + + +public class CookieUtils { + + public static Optional getCookie(HttpServletRequest request, String name) { + Cookie[] cookies = request.getCookies(); + if (cookies != null && cookies.length > 0) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + return Optional.of(cookie); + } + } + } + return Optional.empty(); + } + + public static void addCookie(HttpServletResponse response, String name, String value, + int maxAge) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + cookie.setHttpOnly(true); + cookie.setMaxAge(maxAge); + response.addCookie(cookie); + } + + public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, + String name) { + Cookie[] cookies = request.getCookies(); + if (cookies != null && cookies.length > 0) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + cookie.setValue(""); + cookie.setPath("/"); + cookie.setMaxAge(0); + response.addCookie(cookie); + } + } + } + } + + public static String serialize(Object object) { + return Base64.getUrlEncoder().encodeToString(SerializationUtils.serialize(object)); + } + + public static T deserialize(Cookie cookie, Class cls) { + return cls.cast( + SerializationUtils.deserialize(Base64.getUrlDecoder().decode(cookie.getValue()))); + } +} \ No newline at end of file diff --git a/src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java b/src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java new file mode 100644 index 0000000..0c5ccd1 --- /dev/null +++ b/src/main/java/team1/BE/seamless/util/auth/HttpCookieOAuth2AuthorizationRequestRepository.java @@ -0,0 +1,50 @@ +package team1.BE.seamless.util.auth; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.stereotype.Component; + +@Component +public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository { + + public static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request"; + public static final String REDIRECT_URI_PARAM_COOKIE_NAME = "redirect_uri"; + private static final int cookieExpireSeconds = 180; + + @Override + public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) { + OAuth2AuthorizationRequest oAuth2AuthorizationRequest = CookieUtils.getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME) + .map(cookie -> CookieUtils.deserialize(cookie, OAuth2AuthorizationRequest.class)) + .orElse(null); + return oAuth2AuthorizationRequest; + } + + @Override + public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) { + if (authorizationRequest == null) { + removeAuthorizationRequest(request, response); + return; + } + + CookieUtils.addCookie(response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, CookieUtils.serialize(authorizationRequest), cookieExpireSeconds); + String redirectUriAfterLogin = request.getParameter(REDIRECT_URI_PARAM_COOKIE_NAME); + if (StringUtils.isNotBlank(redirectUriAfterLogin)) { + CookieUtils.addCookie(response, REDIRECT_URI_PARAM_COOKIE_NAME, redirectUriAfterLogin, cookieExpireSeconds); + } + } + + @Override + public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request, + HttpServletResponse response) { + return this.loadAuthorizationRequest(request); + } + + public void removeAuthorizationRequestCookies(HttpServletRequest request, HttpServletResponse response) { + CookieUtils.deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME); + CookieUtils.deleteCookie(request, response, REDIRECT_URI_PARAM_COOKIE_NAME); + } + +} \ No newline at end of file diff --git a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java index 5777bc2..6265af7 100644 --- a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java +++ b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java @@ -24,22 +24,26 @@ public class SecurityConfig { private final TokenAuthenticationFilter tokenAuthenticationFilter; private final TokenExceptionFilter tokenExceptionFilter; private final SecurityEntryPoint SecurityException; + private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository; @Autowired public SecurityConfig(AuthService authService, OAuth2SuccessHandler successHandler, TokenAuthenticationFilter tokenAuthenticationFilter, TokenExceptionFilter tokenExceptionFilter, - SecurityEntryPoint SecurityException) { + SecurityEntryPoint securityException, + HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) { this.authService = authService; this.successHandler = successHandler; this.tokenAuthenticationFilter = tokenAuthenticationFilter; this.tokenExceptionFilter = tokenExceptionFilter; - this.SecurityException = SecurityException; + SecurityException = securityException; + this.authorizationRequestRepository = authorizationRequestRepository; } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity http, + HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .cors(AbstractHttpConfigurer::disable) @@ -72,8 +76,12 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .oauth2Login(oauth -> oauth .userInfoEndpoint(c -> c.userService(authService)) .successHandler(successHandler) + .authorizationEndpoint() + .baseUri("/login/oauth2/code/*") + .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository) ) + // .exceptionHandling(handler -> handler.authenticationEntryPoint(SecurityException)) .addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) From 1d82ce2c042cae847fed8828f17396d6cc8e00df Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:07:54 +0900 Subject: [PATCH 59/63] =?UTF-8?q?fix:=20valid=EB=8A=94=20service=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A0=81=EC=9A=A9=ED=95=A0=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/team1/BE/seamless/service/TaskService.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/team1/BE/seamless/service/TaskService.java b/src/main/java/team1/BE/seamless/service/TaskService.java index 6af3c8c..eb59232 100644 --- a/src/main/java/team1/BE/seamless/service/TaskService.java +++ b/src/main/java/team1/BE/seamless/service/TaskService.java @@ -7,10 +7,7 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import team1.BE.seamless.DTO.TaskDTO.TaskCreate; -import team1.BE.seamless.DTO.TaskDTO.TaskDetail; -import team1.BE.seamless.DTO.TaskDTO.TaskUpdate; -import team1.BE.seamless.DTO.TaskDTO.getList; +import team1.BE.seamless.DTO.TaskDTO.*; import team1.BE.seamless.entity.MemberEntity; import team1.BE.seamless.entity.ProjectEntity; import team1.BE.seamless.entity.TaskEntity; @@ -60,7 +57,7 @@ public Page getTaskList(Long projectId, getList param) { return taskEntities.map(taskMapper::toDetail); } - public TaskDetail createTask(HttpServletRequest req, @Valid Long projectId, Create create) { + public TaskDetail createTask(HttpServletRequest req, Long projectId, TaskCreate taskCreate) { ProjectEntity project = projectRepository.findByIdAndUserEntityEmailAndIsDeletedFalse( projectId, parsingPram.getEmail(req)) .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "존재하지 않는 프로젝트")); From 4e53089282d7924e44bc93df3414057394c6afb0 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Fri, 18 Oct 2024 16:22:25 +0900 Subject: [PATCH 60/63] =?UTF-8?q?feat=20:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=EC=97=90=20=EB=8C=80=ED=95=9C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team1/BE/seamless/ProjectServiceTest.java | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/test/java/team1/BE/seamless/ProjectServiceTest.java diff --git a/src/test/java/team1/BE/seamless/ProjectServiceTest.java b/src/test/java/team1/BE/seamless/ProjectServiceTest.java new file mode 100644 index 0000000..8c9f12d --- /dev/null +++ b/src/test/java/team1/BE/seamless/ProjectServiceTest.java @@ -0,0 +1,157 @@ +package team1.BE.seamless; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.http.HttpMethod.DELETE; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.OK; + +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import team1.BE.seamless.DTO.ProjectDTO.ProjectCreate; +import team1.BE.seamless.DTO.ProjectDTO.ProjectUpdate; +import team1.BE.seamless.service.ProjectService; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class ProjectServiceTest { + @LocalServerPort + private int port; + private String url = "http://localhost:"; + private final TestRestTemplate restTemplate; + private String token; + private HttpHeaders headers = new HttpHeaders(); + private final ProjectService projectService; + @Autowired + public ProjectServiceTest(TestRestTemplate restTemplate, ProjectService projectService) { + this.restTemplate = restTemplate; + this.projectService = projectService; + } + /** + * 특정 유저id의 토큰 파싱 + */ + @BeforeEach + public void setUp() { + HttpEntity requestEntity = new HttpEntity<>(null); + ResponseEntity responseEntity = restTemplate.exchange( + url + port + "/api/test/userToken/1", + POST, + requestEntity, String.class); + int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + int endIndex = responseEntity.getBody().indexOf("\"", startIndex); + token = responseEntity.getBody().substring(startIndex, endIndex); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setBearerAuth(token); + } + + @Test + void 프로젝트_생성_성공() { + ProjectCreate body = new ProjectCreate("프로젝트 이름2", + LocalDateTime.of(2024, 11, 1, 1, 1, 1), + LocalDateTime.of(2024, 11, 4, 4, 4, 4), + List.of(2L, 3L)); + HttpEntity requestEntity = new HttpEntity(body, headers); + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project", + POST, + requestEntity, + String.class); + System.out.println(responseEntity.getBody()); + assertThat(responseEntity.getStatusCode()).isEqualTo(OK); + } + + @Test + void 프로젝트_날짜_수정_성공() { + ProjectUpdate body = new ProjectUpdate("프로젝트 이름1", List.of(1L, 2L, 3L), + LocalDateTime.of(2024, 10, 1, 0, 0, 0), + LocalDateTime.of(2024, 10, 4, 4, 4, 4)); + HttpEntity requestEntity = new HttpEntity(body, headers); + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1", + PUT, + requestEntity, + String.class); + System.out.println(responseEntity.getBody()); + assertThat(responseEntity.getStatusCode()).isEqualTo(OK); + } + + /** + * + * 이 부분 전에 ProjectDTO에서 startDate, endDate 생성 시 현재 이전 값도 되야 되지 않나? + * 라고 말했던 적 있어서 일단 만들어 놓고 주석처리 + * 현재는 @Future가 설정 안되어 잇어서 과거의 값도 생성 가능한 상태 + */ +// @Test +// void 프로젝트_날짜_현재_이전_불가() { +// // 현재보다 이전인 날짜 설정 +// ProjectUpdate body = new ProjectUpdate("프로젝트 이름", List.of(1L, 2L, 3L), +// LocalDateTime.of(2022, 10, 1, 0, 0), +// LocalDateTime.of(2022, 10, 5, 0, 0)); +// +// HttpEntity requestEntity = new HttpEntity<>(body, headers); +// ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1", +// PUT, requestEntity, String.class); +// +// assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); +// } + + @Test + void 프로젝트_종료일_시작일_이전_불가() { + // 종료일이 시작일보다 이전인 날짜 설정 + ProjectUpdate body = new ProjectUpdate("프로젝트 이름", List.of(1L, 2L, 3L), + LocalDateTime.of(2024, 10, 10, 0, 0), + LocalDateTime.of(2024, 10, 5, 0, 0)); + + HttpEntity requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1", + PUT, requestEntity, String.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + } + + @Test + void 프로젝트_생성시_시작일과_종료일_간견은_1일_이상_필수() { + // 1일보다 작은 갭을 설정 + ProjectUpdate body = new ProjectUpdate("프로젝트 이름", List.of(1L, 2L, 3L), + LocalDateTime.of(2024, 10, 10, 0, 0), + LocalDateTime.of(2024, 10, 10, 23, 59)); + + HttpEntity requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1", + PUT, requestEntity, String.class); + + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + } + + /** + * 이 부분도 지금은 그냥 Service가 delete시 isDeleted = true로 변경하고 + * 그냥 id를 반환하는 구조인데, 이를 체크하기 위해서 isDeleted를 포함한 + * DTO를 반환하는 방법은 좋지 않은 것 같아서 일단을 이렇게 구현*/ + @Test + void 삭제된_프로젝트_조회_불가() { + HttpEntity requestEntity = new HttpEntity(null, headers); + + restTemplate.exchange(url + port + "/api/project/1", + DELETE, + requestEntity, String.class); + + ResponseEntity responseEntity = restTemplate.exchange(url + port + "/api/project/1", + DELETE, + requestEntity, String.class); + + System.out.println(responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + } + + +} + + + From 99606f7a1a2b473fb20c202980d38f620d4b0a33 Mon Sep 17 00:00:00 2001 From: sunandrabbit Date: Fri, 18 Oct 2024 16:22:59 +0900 Subject: [PATCH 61/63] =?UTF-8?q?fix=20:=20CI/CD=20=EB=B8=8C=EB=9E=9C?= =?UTF-8?q?=EC=B9=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 64e2f21..03b0f36 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,9 +2,9 @@ name: CI/CD Pipeline on: push: - branches: [ week6 ] + branches: [ develop ] pull_request: - branches: [ week6 ] + branches: [ develop ] env: LIGHTSAIL_USERNAME: ubuntu From dd90da4e9dd0052069684f267cec029778074432 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:43:45 +0900 Subject: [PATCH 62/63] =?UTF-8?q?fix:=20MemberServiceTest=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BE/seamless/DTO/MemberResponseDTO.java | 14 +++ .../BE/seamless/mapper/MemberMapper.java | 5 +- .../BE/seamless/service/MemberService.java | 38 +++---- .../team1/BE/seamless/util/auth/JwtToken.java | 2 +- .../BE/seamless/util/auth/SecurityConfig.java | 4 +- .../seamless/service/MemberServiceTest.java | 99 ++++++++++++++----- 6 files changed, 113 insertions(+), 49 deletions(-) diff --git a/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java b/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java index 4268dd1..99d35a6 100644 --- a/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java +++ b/src/main/java/team1/BE/seamless/DTO/MemberResponseDTO.java @@ -10,6 +10,8 @@ public class MemberResponseDTO { private String email; + private String code; + public MemberResponseDTO(String message, String name, String role, String email) { this.message = message; this.name = name; @@ -17,6 +19,14 @@ public MemberResponseDTO(String message, String name, String role, String email) this.email = email; } + public MemberResponseDTO(String message, String name, String role, String email, String code) { + this.message = message; + this.name = name; + this.role = role; + this.email = email; + this.code = code; + } + public String getRole() { return role; } @@ -48,4 +58,8 @@ public String getMessage() { public void setMessage(String message) { this.message = message; } + + public String getCode() { + return code; + } } diff --git a/src/main/java/team1/BE/seamless/mapper/MemberMapper.java b/src/main/java/team1/BE/seamless/mapper/MemberMapper.java index 995cf84..add4ac6 100644 --- a/src/main/java/team1/BE/seamless/mapper/MemberMapper.java +++ b/src/main/java/team1/BE/seamless/mapper/MemberMapper.java @@ -43,11 +43,12 @@ public MemberResponseDTO toDeleteResponseDTO(MemberEntity memberEntity) { memberEntity.getEmail()); } - public MemberResponseDTO toCreateResponseDTO(MemberEntity memberEntity) { + public MemberResponseDTO toCreateResponseDTO(MemberEntity memberEntity, String code) { return new MemberResponseDTO("성공적으로 생성되었습니다.", memberEntity.getName(), memberEntity.getRole(), - memberEntity.getEmail()); + memberEntity.getEmail(), + code); } public MemberResponseDTO toPutResponseDTO(MemberEntity memberEntity) { diff --git a/src/main/java/team1/BE/seamless/service/MemberService.java b/src/main/java/team1/BE/seamless/service/MemberService.java index 70e63cd..9ff998b 100644 --- a/src/main/java/team1/BE/seamless/service/MemberService.java +++ b/src/main/java/team1/BE/seamless/service/MemberService.java @@ -74,22 +74,22 @@ public Page getMemberList(@Valid Long projectId, memberListRequestDTO.toPageable()); } - public MemberResponseDTO createMember(Long projectId, CreateMember create, HttpServletRequest req) { - // 팀원인지 확인.. 삭제함 - - ProjectEntity project = projectRepository.findById(projectId) - .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); - - // 아래는 프로젝트가 종료됐는데, 그 후에 팀원이 참여링크를 통해 프로젝트 참여를 했을 때 걸러내는거임ㅇㅇ -// if (project.getEndDate().isBefore(LocalDateTime.now())) { -// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); -// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ - - MemberEntity member = memberMapper.toEntity(create, project); - memberRepository.save(member); - - return memberMapper.toCreateResponseDTO(member); - } +// public MemberResponseDTO createMember(Long projectId, CreateMember create, HttpServletRequest req) { +// // 팀원인지 확인.. 삭제함 +// +// ProjectEntity project = projectRepository.findById(projectId) +// .orElseThrow(() -> new BaseHandler(HttpStatus.NOT_FOUND, "해당 프로젝트가 존재하지 않습니다.")); +// +// // 아래는 프로젝트가 종료됐는데, 그 후에 팀원이 참여링크를 통해 프로젝트 참여를 했을 때 걸러내는거임ㅇㅇ +//// if (project.getEndDate().isBefore(LocalDateTime.now())) { +//// throw new BaseHandler(HttpStatus.BAD_REQUEST, "프로젝트는 종료되었습니다."); +//// } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ +// +// MemberEntity member = memberMapper.toEntity(create, project); +// memberRepository.save(member); +// +// return memberMapper.toCreateResponseDTO(member); +// } @Transactional public MemberResponseDTO createMember(CreateMember create) { @@ -124,7 +124,7 @@ public MemberResponseDTO createMember(CreateMember create) { // 이메일로 코드 전달(추가 요망) - return memberMapper.toCreateResponseDTO(member); + return memberMapper.toCreateResponseDTO(member, code); } @Transactional @@ -158,8 +158,8 @@ public MemberResponseDTO deleteMember(Long projectId, Long memberId, HttpServlet // } 프로젝트 initData에 EndDate 설정이 안되어있어서 지금 테스트하면 오류걸림 그래서 주석처리 해놓음ㅇㅇ // 팀장인지 확인(팀원인지 굳이 한번 더 확인하지 않음. 팀장인지만 검증.) - if (parsingPram.getRole(req).equals(Role.USER.toString())) { - throw new BaseHandler(HttpStatus.UNAUTHORIZED,"수정 권한이 없습니다."); + if (parsingPram.getRole(req).equals(Role.MEMBER.toString())) { + throw new BaseHandler(HttpStatus.FORBIDDEN,"수정 권한이 없습니다."); } MemberEntity member = memberRepository.findByProjectEntityIdAndIdAndIsDeleteFalse( diff --git a/src/main/java/team1/BE/seamless/util/auth/JwtToken.java b/src/main/java/team1/BE/seamless/util/auth/JwtToken.java index bf93b4e..c596b7e 100644 --- a/src/main/java/team1/BE/seamless/util/auth/JwtToken.java +++ b/src/main/java/team1/BE/seamless/util/auth/JwtToken.java @@ -83,7 +83,7 @@ public String createUserToken(UserEntity user) { ZonedDateTime expirationDateTime = now.plusSeconds(tokenExpTime); Claims claims = Jwts.claims(); - claims.put("authentication", Role.MEMBER.toString()); + claims.put("authentication", Role.USER.toString()); claims.put("email", user.getEmail()); return Jwts.builder() .setClaims(claims) diff --git a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java index 6265af7..6afb5e2 100644 --- a/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java +++ b/src/main/java/team1/BE/seamless/util/auth/SecurityConfig.java @@ -66,9 +66,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http, // 인증, h2 .requestMatchers("/h2-console/**", "/auth/**","/api/test/**").permitAll() // 멤버 생성 - .requestMatchers(HttpMethod.POST,"/api/project/{project_id}/member/**").permitAll() + .requestMatchers(HttpMethod.POST,"/api/project/**/member/**").permitAll() // 멤버 조회 - .requestMatchers(HttpMethod.GET,"/api/project/{project_id}/member/**").permitAll() + .requestMatchers(HttpMethod.GET,"/api/project/**/member/**").permitAll() .anyRequest() .authenticated() ) diff --git a/src/test/java/team1/BE/seamless/service/MemberServiceTest.java b/src/test/java/team1/BE/seamless/service/MemberServiceTest.java index b9b1fb7..384c04b 100644 --- a/src/test/java/team1/BE/seamless/service/MemberServiceTest.java +++ b/src/test/java/team1/BE/seamless/service/MemberServiceTest.java @@ -1,9 +1,11 @@ package team1.BE.seamless.service; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpMethod.DELETE; import static org.springframework.http.HttpMethod.GET; -import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpMethod.POST; +import static org.springframework.http.HttpMethod.PUT; +import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; @@ -17,10 +19,9 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; - +import team1.BE.seamless.DTO.MemberRequestDTO.CreateMember; import team1.BE.seamless.DTO.MemberRequestDTO.UpdateMember; import team1.BE.seamless.entity.MemberEntity; -import team1.BE.seamless.service.MemberService; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class MemberServiceTest { @@ -30,29 +31,57 @@ class MemberServiceTest { private String url = "http://localhost:"; private final TestRestTemplate restTemplate; private String token; + private String memberToken; private HttpHeaders headers = new HttpHeaders(); - private final MemberService memberService; @Autowired - public MemberServiceTest(TestRestTemplate restTemplate, MemberService memberService) { + public MemberServiceTest(TestRestTemplate restTemplate) { this.restTemplate = restTemplate; - this.memberService = memberService; } @BeforeEach public void setUp() { +// 새로운 멤버 생성 + CreateMember member = new CreateMember("ex@gmail.com","cCeJvA99H7bV2ctvVIpM4Bh3ZJvawh3JnX3tREWGtNA="); + HttpEntity request1 = new HttpEntity<>(member); + ResponseEntity response1 = restTemplate.exchange( + url + port + "/api/project/1/member", + POST, + request1, String.class); + + int startIndex = response1.getBody().indexOf("\"code\":\"") + "\"code\":\"".length(); + int endIndex = response1.getBody().indexOf("\"", startIndex); + +// 멤버 생성시 반환되는 코드 추출 + String code = response1.getBody().substring(startIndex, endIndex); + +// 코드로 멤버 토큰 요청 + HttpEntity request2 = new HttpEntity<>(null); + ResponseEntity response2 = restTemplate.exchange( + url + port + "/api/auth/memberCode?memberCode=" + code, + GET, + request2, String.class); + + startIndex = response2.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + endIndex = response2.getBody().indexOf("\"", startIndex); + + memberToken = response2.getBody().substring(startIndex, endIndex); + +// 팀장의 토큰 요청 HttpEntity requestEntity = new HttpEntity<>(null); ResponseEntity responseEntity = restTemplate.exchange( - url + port + "/api/test/userToken/1", - PUT, - requestEntity, String.class); + url + port + "/api/test/userToken/1", + POST, + requestEntity, String.class); + + startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); + endIndex = responseEntity.getBody().indexOf("\"", startIndex); - int startIndex = responseEntity.getBody().indexOf("\"token\":\"") + "\"token\":\"".length(); - int endIndex = responseEntity.getBody().indexOf("\"", startIndex); token = responseEntity.getBody().substring(startIndex, endIndex); headers.setContentType(MediaType.APPLICATION_JSON); - headers.setBearerAuth(token); + headers.setBearerAuth(memberToken); + this.url += port + "/api/project/1/member/"; } @@ -69,26 +98,46 @@ void updateMemberWithEmptyEmailShouldFail() { requestEntity, String.class); - assertThat(response.getStatusCode()).isEqualTo(BAD_REQUEST); + assertThat(response.getStatusCode()).isEqualTo(OK); } + // 멤버가 멤버를 삭제 가능한에 대한 테스트 + @Test + void softDeleteMember() { + headers.setBearerAuth(memberToken); + // Soft delete member + HttpEntity requestEntity = new HttpEntity<>(null, headers); + ResponseEntity response = restTemplate.exchange( + url + "1", // 테스트 Member ID + DELETE, + requestEntity, + String.class); + + assertThat(response.getStatusCode()).isEqualTo(FORBIDDEN); + } // "멤버 삭제(softdelete로 되는가, 다시 조회하면 조회 되는가)" 에 대한 테스트 @Test void softDeleteMemberAndRequery() { - // Soft delete member - restTemplate.exchange(url + "1", // 테스트 Member ID - PUT, - new HttpEntity<>(new UpdateMember("", "팀원", "email@example.com", "http://example.com", true), headers), - Void.class); + headers.setBearerAuth(token); - ResponseEntity response = restTemplate.exchange( - url + "123", - GET, - new HttpEntity<>(headers), - MemberEntity.class); + HttpEntity requestEntity = new HttpEntity<>(null, headers); - assertThat(response.getStatusCode()).isEqualTo(NOT_FOUND); + // Soft delete member + ResponseEntity response1 = restTemplate.exchange( + url + "2", // 테스트 Member ID + DELETE, + requestEntity, + String.class); + assertThat(response1.getStatusCode()).isEqualTo(OK); + + ResponseEntity response2 = restTemplate.exchange( + url + "2", + GET, + new HttpEntity<>(headers), + MemberEntity.class); + + assertThat(response2.getStatusCode()).isEqualTo(NOT_FOUND); } } From 4800e5b2ad3a46db67b5059a25a24d4bff16a846 Mon Sep 17 00:00:00 2001 From: Kim-DongHyuk <80240164+Kdonghs@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:45:01 +0900 Subject: [PATCH 63/63] =?UTF-8?q?fix:=20=EC=84=9C=EB=B2=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=8F=84=20dev=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b247740..a349acf 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -17,7 +17,7 @@ spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect jwt.secretKey=KO)$*"g+zWechI9]KE|'irIM?ky--x/|p.K&>bA>Ef,gfD/)ekd/UcBE2kQ@BhE jwt.tokenExpTime=3600 code.secretKey=ae2af8cfb8721e39189f6f5edc928589 -spring.profiles.active=build +spring.profiles.active=dev server.forward-headers-strategy=framework spring.profiles.include=oauth spring.mvc.pathmatch.matching-strategy=ant_path_matcher