From 9562232846c8214bb48b978ee87e0a15c275677d Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Sun, 13 Oct 2024 01:27:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=9D=98=EB=A2=B0=20=EC=BD=94=EB=A9=98=ED=8A=B8=20?= =?UTF-8?q?CRUD=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/CommentController.java | 94 +++++++++++++++++++ .../comment/dto/CommentRegistRequestDTO.java | 12 +++ .../comment/dto/CommentUpdateRequestDTO.java | 13 +++ .../comment/entity/Comment.java | 53 +++++++++++ .../comment/repository/CommentRepository.java | 14 +++ .../comment/service/CommentService.java | 66 +++++++++++++ .../devlinkbackend/ether/entity/Ether.java | 2 +- .../devlinkbackend/oauth2/entity/User.java | 2 +- .../controller/QuestionController.java | 20 ++-- .../question/entity/Question.java | 2 +- .../reply/controller/ReplyController.java | 14 +-- .../devlinkbackend/reply/entity/Reply.java | 5 +- .../request/controller/RequestController.java | 8 +- .../request/entity/Request.java | 12 ++- 14 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java create mode 100644 src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java new file mode 100644 index 0000000..638748d --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java @@ -0,0 +1,94 @@ +package com.mtvs.devlinkbackend.comment.controller; + +import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; +import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; +import com.mtvs.devlinkbackend.comment.entity.Comment; +import com.mtvs.devlinkbackend.comment.service.CommentService; +import com.mtvs.devlinkbackend.config.JwtUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/comment") +public class CommentController { + private final CommentService commentService; + private final JwtUtil jwtUtil; + + public CommentController(CommentService commentService, JwtUtil jwtUtil) { + this.commentService = commentService; + this.jwtUtil = jwtUtil; + } + + @Operation(summary = "댓글 등록", description = "특정 요청에 대한 새로운 댓글을 등록합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "댓글이 성공적으로 등록되었습니다."), + @ApiResponse(responseCode = "404", description = "해당 요청을 찾을 수 없습니다.") + }) + @PostMapping + public ResponseEntity registComment( + @RequestBody CommentRegistRequestDTO commentRegistRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + Comment comment = commentService.registComment(commentRegistRequestDTO, accountId); + return ResponseEntity.status(HttpStatus.CREATED).body(comment); + } + + @Operation(summary = "댓글 조회", description = "ID를 사용하여 댓글을 조회합니다.") + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 조회되었습니다.") + @GetMapping("/{commentId}") + public ResponseEntity findCommentByCommentId(@PathVariable Long commentId) { + Comment comment = commentService.findCommentByCommentId(commentId); + return ResponseEntity.ok(comment); + } + + @Operation(summary = "요청 ID로 댓글 조회", description = "특정 요청에 연관된 모든 댓글을 조회합니다.") + @ApiResponse(responseCode = "200", description = "댓글 목록이 성공적으로 조회되었습니다.") + @GetMapping("/request/{requestId}") + public ResponseEntity> findCommentsByRequestId(@PathVariable Long requestId) { + List comments = commentService.findCommentsByRequestId(requestId); + return ResponseEntity.ok(comments); + } + + @Operation(summary = "사용자 ID로 댓글 조회", description = "특정 사용자가 작성한 모든 댓글을 조회합니다.") + @ApiResponse(responseCode = "200", description = "사용자의 댓글 목록이 성공적으로 조회되었습니다.") + @GetMapping("/account") + public ResponseEntity> findCommentsByAccountId( + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + List comments = commentService.findCommentsByAccountId(accountId); + return ResponseEntity.ok(comments); + } + + @Operation(summary = "댓글 수정", description = "기존 댓글의 내용을 수정합니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 수정되었습니다."), + @ApiResponse(responseCode = "403", description = "댓글을 수정할 권한이 없습니다."), + @ApiResponse(responseCode = "404", description = "해당 댓글을 찾을 수 없습니다.") + }) + @PatchMapping + public ResponseEntity updateComment( + @RequestBody CommentUpdateRequestDTO commentUpdateRequestDTO, + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { + + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); + Comment updatedComment = commentService.updateComment(commentUpdateRequestDTO, accountId); + return ResponseEntity.ok(updatedComment); + } + + @Operation(summary = "댓글 삭제", description = "ID를 사용하여 댓글을 삭제합니다.") + @ApiResponse(responseCode = "204", description = "댓글이 성공적으로 삭제되었습니다.") + @DeleteMapping("/{commentId}") + public ResponseEntity deleteComment(@PathVariable Long commentId) { + + commentService.deleteComment(commentId); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java new file mode 100644 index 0000000..a2de63c --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentRegistRequestDTO.java @@ -0,0 +1,12 @@ +package com.mtvs.devlinkbackend.comment.dto; + +import lombok.*; + +@Getter @Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class CommentRegistRequestDTO { + private String content; + private Long requestId; +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java new file mode 100644 index 0000000..a82cfd6 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/dto/CommentUpdateRequestDTO.java @@ -0,0 +1,13 @@ +package com.mtvs.devlinkbackend.comment.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class CommentUpdateRequestDTO { + private Long commentId; + private String content; +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java b/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java new file mode 100644 index 0000000..a596086 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java @@ -0,0 +1,53 @@ +package com.mtvs.devlinkbackend.comment.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import com.mtvs.devlinkbackend.request.entity.Request; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.time.LocalDateTime; + +@Table(name = "COMMENT") +@Entity(name = "Comment") +@NoArgsConstructor +@Getter +@ToString +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "COMMENT_ID") + private Long commentId; + + @Column(name = "CONTENT") + private String content; + + @Column(name = "ACCOUNT_ID") + private String accountId; + + @CreationTimestamp + @Column(name = "CREATED_AT", updatable = false) + private LocalDateTime createdAt; + + @UpdateTimestamp + @Column(name = "MODIFIED_AT") + private LocalDateTime modifiedAt; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "REQUEST_ID", nullable = false) + @JsonBackReference + private Request request; + + public Comment(String content, String accountId, Request request) { + this.content = content; + this.accountId = accountId; + this.request = request; + } + + public void setContent(String content) { + this.content = content; + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java b/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java new file mode 100644 index 0000000..36e24c2 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java @@ -0,0 +1,14 @@ +package com.mtvs.devlinkbackend.comment.repository; + +import com.mtvs.devlinkbackend.comment.entity.Comment; +import com.mtvs.devlinkbackend.request.entity.Request; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CommentRepository extends JpaRepository { + List findCommentsByAccountId(String accountId); + List findCommentsByRequest_RequestId(Long requestId); +} diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java b/src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java new file mode 100644 index 0000000..c1bb6f0 --- /dev/null +++ b/src/main/java/com/mtvs/devlinkbackend/comment/service/CommentService.java @@ -0,0 +1,66 @@ +package com.mtvs.devlinkbackend.comment.service; + +import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; +import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; +import com.mtvs.devlinkbackend.comment.entity.Comment; +import com.mtvs.devlinkbackend.comment.repository.CommentRepository; +import com.mtvs.devlinkbackend.request.entity.Request; +import com.mtvs.devlinkbackend.request.repository.RequestRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +@Service +public class CommentService { + private final CommentRepository commentRepository; + private final RequestRepository requestRepository; + + public CommentService(CommentRepository commentRepository, RequestRepository requestRepository) { + this.commentRepository = commentRepository; + this.requestRepository = requestRepository; + } + + @Transactional + public Comment registComment(CommentRegistRequestDTO commentRegistRequestDTO, String accountId) { + Request request = requestRepository.findById(commentRegistRequestDTO.getRequestId()).orElse(null); + return commentRepository.save(new Comment( + commentRegistRequestDTO.getContent(), + accountId, + request + )); + } + + public Comment findCommentByCommentId(Long commentId) { + return commentRepository.findById(commentId).orElse(null); + } + + public List findCommentsByRequestId(Long requestId) { + return commentRepository.findCommentsByRequest_RequestId(requestId); + } + + public List findCommentsByAccountId(String accountId) { + return commentRepository.findCommentsByAccountId(accountId); + } + + @Transactional + public Comment updateComment(CommentUpdateRequestDTO commentUpdateRequestDTO, String accountId) { + Optional comment = commentRepository.findById(commentUpdateRequestDTO.getCommentId()); + if (comment.isPresent()) { + Comment foundComment = comment.get(); + if(foundComment.getAccountId().equals(accountId)) { + foundComment.setContent(commentUpdateRequestDTO.getContent()); + return foundComment; + } + else throw new IllegalArgumentException("다른 사용자가 코멘트 수정 시도 / commentId : " + + commentUpdateRequestDTO.getCommentId() + + ", accountId : " + accountId); + } + else throw new IllegalArgumentException("잘못된 commentId로 코멘트 수정 시도"); + } + + public void deleteComment(Long commentId) { + commentRepository.deleteById(commentId); + } +} diff --git a/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java b/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java index 0c8464a..2177ac9 100644 --- a/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java +++ b/src/main/java/com/mtvs/devlinkbackend/ether/entity/Ether.java @@ -9,7 +9,7 @@ import java.time.LocalDateTime; @Table(name = "ETHER") -@Entity(name = "ETHER") +@Entity(name = "Ether") @Getter @ToString public class Ether { diff --git a/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java b/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java index d710168..e6b2eb6 100644 --- a/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java +++ b/src/main/java/com/mtvs/devlinkbackend/oauth2/entity/User.java @@ -8,7 +8,7 @@ import java.time.LocalDateTime; @Table(name = "USER") -@Entity(name = "USER") +@Entity(name = "User") @Getter public class User { @Id diff --git a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java index da7343c..52e3427 100644 --- a/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java +++ b/src/main/java/com/mtvs/devlinkbackend/question/controller/QuestionController.java @@ -40,15 +40,14 @@ public QuestionController(QuestionService questionService, JwtUtil jwtUtil) { }) public ResponseEntity createQuestion( @RequestBody QuestionRegistRequestDTO questionRegistRequestDTO, - @RequestHeader(name = "Authorization") String accessToken) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithAuth(authorizationHeader); Question createdQuestion = questionService.registQuestion(questionRegistRequestDTO, accountId); return ResponseEntity.ok(createdQuestion); } // Retrieve a question by ID - @GetMapping("/{questionId}") @Operation( summary = "PK에 따른 질문 조회", description = "PK값으로 사용자가 올린 공개 질문 1개를 조회한다." @@ -58,6 +57,7 @@ public ResponseEntity createQuestion( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @GetMapping("/{questionId}") public ResponseEntity getQuestionById(@PathVariable Long questionId) { Question question = questionService.findQuestionByQuestionId(questionId); if (question != null) { @@ -84,7 +84,6 @@ public ResponseEntity> getAllQuestionsWithPaging(@RequestParam in } // Retrieve questions by account ID with pagination - @GetMapping @Operation( summary = "Pagination으로 로그인한 사용자의 질문 조회", description = "Pagination으로 사용자가 했던 질문 전체를 조회한다. 최대 20개가 주어진다" @@ -94,17 +93,17 @@ public ResponseEntity> getAllQuestionsWithPaging(@RequestParam in @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @GetMapping("/account") public ResponseEntity> getQuestionsByAccountIdWithPaging( @RequestParam int page, - @RequestHeader(name = "Authorization") String accessToken) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); List questions = questionService.findQuestionsByAccountIdWithPaging(page, accountId); return ResponseEntity.ok(questions); } // Update a question by ID - @PatchMapping("/{id}") @Operation( summary = "사용자 질문 수정", description = "사용자가 했던 질문을 수정한다." @@ -114,11 +113,12 @@ public ResponseEntity> getQuestionsByAccountIdWithPaging( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @PatchMapping public ResponseEntity updateQuestion( @RequestBody QuestionUpdateRequestDTO questionUpdateRequestDTO, - @RequestHeader(name = "Authorization") String accessToken) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(accessToken); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); try { Question updatedQuestion = questionService.updateQuestion(questionUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedQuestion); @@ -128,7 +128,6 @@ public ResponseEntity updateQuestion( } // Delete a question by ID - @DeleteMapping("/{questionId}") @Operation( summary = "사용자 질문 삭제", description = "사용자가 했던 질문을 삭제한다" @@ -138,6 +137,7 @@ public ResponseEntity updateQuestion( @ApiResponse(responseCode = "400", description = "잘못된 헤더 또는 파라미터 전달"), @ApiResponse(responseCode = "401", description = "인증되지 않음") }) + @DeleteMapping("/{questionId}") public ResponseEntity deleteQuestion(@PathVariable Long questionId) { questionService.deleteQuestion(questionId); diff --git a/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java b/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java index 911f374..fd82d90 100644 --- a/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java +++ b/src/main/java/com/mtvs/devlinkbackend/question/entity/Question.java @@ -14,7 +14,7 @@ import java.util.List; @Table(name = "QUESTION") -@Entity(name = "QUESTION") +@Entity(name = "Question") @Getter @NoArgsConstructor public class Question { diff --git a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java index d1ec63f..ef2014a 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/controller/ReplyController.java @@ -34,9 +34,9 @@ public ReplyController(ReplyService replyService, JwtUtil jwtUtil) { @PostMapping public ResponseEntity registReply( @RequestBody ReplyRegistRequestDTO replyRegistRequestDTO, - @RequestHeader(name = "Authorization") String token) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithAuth(token); + String accountId = jwtUtil.getSubjectFromTokenWithAuth(authorizationHeader); Reply reply = replyService.registReply(replyRegistRequestDTO, accountId); return ResponseEntity.ok(reply); } @@ -71,11 +71,11 @@ public ResponseEntity> findRepliesByQuestionId(@PathVariable Long qu @ApiResponse(responseCode = "401", description = "인증되지 않음"), @ApiResponse(responseCode = "404", description = "댓글을 찾을 수 없음") }) - @GetMapping("/account/{accountId}") + @GetMapping("/account") public ResponseEntity> findRepliesByAccountId( - @RequestHeader(name = "Authorization") String token) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(token); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); List replies = replyService.findRepliesByAccountId(accountId); return ResponseEntity.ok(replies); } @@ -89,9 +89,9 @@ public ResponseEntity> findRepliesByAccountId( @PatchMapping public ResponseEntity updateReply( @RequestBody ReplyUpdateRequestDTO replyUpdateRequestDTO, - @RequestHeader(name = "Authorization") String token) throws Exception { + @RequestHeader(name = "Authorization") String authorizationHeader) throws Exception { - String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(token); + String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); try { Reply updatedReply = replyService.updateReply(replyUpdateRequestDTO, accountId); return ResponseEntity.ok(updatedReply); diff --git a/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java b/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java index 5b28a9f..6683c3b 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java @@ -5,14 +5,13 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; -import net.minidev.json.annotate.JsonIgnore; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import java.time.LocalDateTime; @Table(name = "REPLY") -@Entity(name = "REPLY") +@Entity(name = "Reply") @Getter @NoArgsConstructor public class Reply { @@ -26,7 +25,7 @@ public class Reply { private String content; @CreationTimestamp - @Column(name = "CREATED_AT") + @Column(name = "CREATED_AT", updatable = false) private LocalDateTime createdAt; @UpdateTimestamp diff --git a/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java b/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java index 0a1f791..adae769 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/controller/RequestController.java @@ -75,8 +75,11 @@ public ResponseEntity> getRequestsByAccountId( @ApiResponse(responseCode = "200", description = "의뢰 목록이 성공적으로 조회됨") }) @GetMapping("/date-range") - public ResponseEntity> getRequestsBetweenDates(@RequestParam LocalDateTime startDateTime, @RequestParam LocalDateTime endDateTime) { - List requests = requestService.findAllRequestsBetweenStarDateTimeAndEndDateTime(startDateTime, endDateTime); + public ResponseEntity> getRequestsBetweenDates( + @RequestParam LocalDateTime startDateTime, @RequestParam LocalDateTime endDateTime) { + + List requests = + requestService.findAllRequestsBetweenStarDateTimeAndEndDateTime(startDateTime, endDateTime); return ResponseEntity.ok(requests); } @@ -89,6 +92,7 @@ public ResponseEntity> getRequestsBetweenDates(@RequestParam Local public ResponseEntity updateRequest( @RequestBody RequestUpdateRequestDTO requestDTO, @RequestHeader(name = "Authorization") String authorizationHeader) { + try { String accountId = jwtUtil.getSubjectFromTokenWithoutAuth(authorizationHeader); Request updatedRequest = requestService.updateRequest(requestDTO, accountId); diff --git a/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java b/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java index ad04f8c..ad9e334 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/entity/Request.java @@ -1,5 +1,7 @@ package com.mtvs.devlinkbackend.request.entity; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import com.mtvs.devlinkbackend.comment.entity.Comment; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -8,9 +10,11 @@ import org.hibernate.annotations.UpdateTimestamp; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @Table(name = "REQUEST") -@Entity(name = "REQUEST") +@Entity(name = "Request") @NoArgsConstructor @ToString @Getter @@ -36,13 +40,17 @@ public class Request { private String accountId; @CreationTimestamp - @Column(name = "CREATED_AT") + @Column(name = "CREATED_AT", updatable = false) private LocalDateTime createdAt; @UpdateTimestamp @Column(name = "MODIFIED_AT") private LocalDateTime modifiedAt; + @OneToMany(mappedBy = "request", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference + private List comments = new ArrayList<>(); + public Request(String title, String content, LocalDateTime startDateTime, LocalDateTime endDateTime, String accountId) { this.title = title; this.content = content; From 7693cb59de2be6c6df427c412f3f8c248e8c637e Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Sun, 13 Oct 2024 13:56:35 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Test:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=9D=98=EB=A2=B0=20=EC=BD=94=EB=A9=98=ED=8A=B8=20?= =?UTF-8?q?CRUD=20TestCode=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/repository/RequestRepository.java | 2 +- .../comment/CommentCRUDTest.java | 93 +++++++++++++++++++ .../request/RequestCRUDTest.java | 2 +- 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java diff --git a/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java b/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java index 1bb3c2f..c8a2675 100644 --- a/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java +++ b/src/main/java/com/mtvs/devlinkbackend/request/repository/RequestRepository.java @@ -13,7 +13,7 @@ public interface RequestRepository extends JpaRepository { List findRequestsByAccountId(String accountId); - @Query("SELECT r FROM REQUEST r WHERE r.startDateTime BETWEEN :startDateTime AND :endDateTime " + + @Query("SELECT r FROM Request r WHERE r.startDateTime BETWEEN :startDateTime AND :endDateTime " + "AND r.endDateTime BETWEEN :startDateTime AND :endDateTime") List findRequestsWithinDateRange( @Param("startDateTime") LocalDateTime startDateTime, diff --git a/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java new file mode 100644 index 0000000..d24dd1f --- /dev/null +++ b/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java @@ -0,0 +1,93 @@ +package com.mtvs.devlinkbackend.comment; + +import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; +import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; +import com.mtvs.devlinkbackend.comment.service.CommentService; +import com.mtvs.devlinkbackend.request.service.RequestService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.stream.Stream; + +@SpringBootTest +@Transactional +public class CommentCRUDTest { + @Autowired + private CommentService commentService; + + private static Stream newComment() { + return Stream.of( + Arguments.of(new CommentRegistRequestDTO("내용0", 1L), "계정0"), + Arguments.of(new CommentRegistRequestDTO("내용00", 2L), "계정00") + ); + } + + private static Stream modifiedComment() { + return Stream.of( + Arguments.of(new CommentUpdateRequestDTO(1L, "내용0"), "계정1"), + Arguments.of(new CommentUpdateRequestDTO(2L, "내용00"), "계정1") + ); + } + + @DisplayName("코멘트 추가 테스트") + @ParameterizedTest + @MethodSource("newComment") + @Order(0) + public void testCreateComment(CommentRegistRequestDTO commentRegistRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> commentService.registComment(commentRegistRequestDTO, accountId)); + } + + @DisplayName("PK로 코멘트 조회 테스트") + @ValueSource(longs = {1,2}) + @ParameterizedTest + @Order(1) + public void testFindCommentByCommentId(long commentId) { + Assertions.assertDoesNotThrow(() -> + System.out.println("Comment = " + commentService.findCommentByCommentId(commentId))); + } + + @DisplayName("계정 ID에 따른 코멘트 조회 테스트") + @ValueSource( strings = {"계정1", "계정2"}) + @ParameterizedTest + @Order(2) + public void testFindCommentsByAccountId(String accountId) { + Assertions.assertDoesNotThrow(() -> + System.out.println("Comment = " + commentService.findCommentsByAccountId(accountId))); + } + + @DisplayName("의뢰 ID에 따른 코멘트 조회 테스트") + @ValueSource( longs = {1,2}) + @ParameterizedTest + @Order(3) + public void testFindCommentsByRequestId(long requestId) { + Assertions.assertDoesNotThrow(() -> + System.out.println("Comment = " + commentService.findCommentsByRequestId(requestId))); + } + + @DisplayName("코멘트 수정 테스트") + @MethodSource("modifiedComment") + @ParameterizedTest + @Order(4) + public void testUpdateComment(CommentUpdateRequestDTO commentUpdateRequestDTO, String accountId) { + Assertions.assertDoesNotThrow(() -> + System.out.println(commentService.updateComment(commentUpdateRequestDTO, accountId))); + } + + @DisplayName("코멘트 삭제 테스트") + @ValueSource(longs = {0,1}) + @ParameterizedTest + @Order(5) + public void testDeleteRequest(long commentId) { + Assertions.assertDoesNotThrow(() -> + commentService.deleteComment(commentId)); + } +} diff --git a/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java index 61a24f8..1f5a169 100644 --- a/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java +++ b/src/test/java/com/mtvs/devlinkbackend/request/RequestCRUDTest.java @@ -41,7 +41,7 @@ private static Stream modifiedRequest() { @ParameterizedTest @MethodSource("newRequest") @Order(0) - public void testCreateQuestion(RequestRegistRequestDTO requestRegistRequestDTO, String accountId) { + public void testCreateRequest(RequestRegistRequestDTO requestRegistRequestDTO, String accountId) { Assertions.assertDoesNotThrow(() -> requestService.registRequest(requestRegistRequestDTO, accountId)); } From 7ed306eeff219b1c6ae874de341613c32b173f4e Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Sun, 13 Oct 2024 14:29:52 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Refactor:=20CommentCRUDTest=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A1=B0=ED=9A=8C=EC=8B=9C=20ToString=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=97=B0=EA=B4=80=EA=B4=80=EA=B3=84=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/mtvs/devlinkbackend/comment/entity/Comment.java | 5 +++-- .../comment/repository/CommentRepository.java | 1 - .../java/com/mtvs/devlinkbackend/config/CorsConfig.java | 6 +----- .../java/com/mtvs/devlinkbackend/config/SecurityConfig.java | 2 +- .../java/com/mtvs/devlinkbackend/reply/entity/Reply.java | 6 ++++-- .../com/mtvs/devlinkbackend/comment/CommentCRUDTest.java | 4 +--- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java b/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java index a596086..36aeae3 100644 --- a/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java +++ b/src/main/java/com/mtvs/devlinkbackend/comment/entity/Comment.java @@ -1,6 +1,7 @@ package com.mtvs.devlinkbackend.comment.entity; import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.mtvs.devlinkbackend.request.entity.Request; import jakarta.persistence.*; import lombok.Getter; @@ -15,7 +16,7 @@ @Entity(name = "Comment") @NoArgsConstructor @Getter -@ToString +@ToString(exclude = "request") // request 필드를 toString에서 제외 public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -38,7 +39,7 @@ public class Comment { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "REQUEST_ID", nullable = false) - @JsonBackReference + @JsonIgnore private Request request; public Comment(String content, String accountId, Request request) { diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java b/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java index 36e24c2..15ffe45 100644 --- a/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java +++ b/src/main/java/com/mtvs/devlinkbackend/comment/repository/CommentRepository.java @@ -1,7 +1,6 @@ package com.mtvs.devlinkbackend.comment.repository; import com.mtvs.devlinkbackend.comment.entity.Comment; -import com.mtvs.devlinkbackend.request.entity.Request; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java index 1cecdac..e502348 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/CorsConfig.java @@ -17,11 +17,7 @@ public CorsConfigurationSource corsConfigurationSource() { configuration.addAllowedOrigin("http://localhost:8080"); configuration.addAllowedOrigin("http://localhost:5173"); // 테스트에서 사용되는 도메인 추가 - configuration.addAllowedMethod("GET"); - configuration.addAllowedMethod("POST"); - configuration.addAllowedMethod("PUT"); - configuration.addAllowedMethod("PATCH"); - configuration.addAllowedMethod("DELETE"); + configuration.addAllowedMethod("*"); configuration.addAllowedHeader("*"); // 모든 헤더 허용 diff --git a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java index 5481a22..d677600 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/SecurityConfig.java @@ -51,7 +51,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers( - "/**", + "/api/**", "/v3/api-docs/**", // Swagger API Docs 경로 "/swagger-ui/**", // Swagger UI 정적 리소스 경로 "/swagger-ui.html", // Swagger UI 페이지 경로 diff --git a/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java b/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java index 6683c3b..9f43a53 100644 --- a/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java +++ b/src/main/java/com/mtvs/devlinkbackend/reply/entity/Reply.java @@ -1,10 +1,11 @@ package com.mtvs.devlinkbackend.reply.entity; -import com.fasterxml.jackson.annotation.JsonBackReference; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.mtvs.devlinkbackend.question.entity.Question; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.ToString; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; @@ -14,6 +15,7 @@ @Entity(name = "Reply") @Getter @NoArgsConstructor +@ToString(exclude = "question") // request 필드를 toString에서 제외 public class Reply { @Id @@ -37,7 +39,7 @@ public class Reply { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "QUESTION_ID", nullable = false) - @JsonBackReference + @JsonIgnore private Question question; public Reply(String content, String accountId, Question question) { diff --git a/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java b/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java index d24dd1f..f408a98 100644 --- a/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java +++ b/src/test/java/com/mtvs/devlinkbackend/comment/CommentCRUDTest.java @@ -3,7 +3,6 @@ import com.mtvs.devlinkbackend.comment.dto.CommentRegistRequestDTO; import com.mtvs.devlinkbackend.comment.dto.CommentUpdateRequestDTO; import com.mtvs.devlinkbackend.comment.service.CommentService; -import com.mtvs.devlinkbackend.request.service.RequestService; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Order; @@ -15,7 +14,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; import java.util.stream.Stream; @SpringBootTest @@ -34,7 +32,7 @@ private static Stream newComment() { private static Stream modifiedComment() { return Stream.of( Arguments.of(new CommentUpdateRequestDTO(1L, "내용0"), "계정1"), - Arguments.of(new CommentUpdateRequestDTO(2L, "내용00"), "계정1") + Arguments.of(new CommentUpdateRequestDTO(2L, "내용00"), "계정2") ); } From 06780d03906529b7118af6a0068a35435737643e Mon Sep 17 00:00:00 2001 From: HEX <123macanic@naver.com> Date: Sun, 13 Oct 2024 14:39:02 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Refactor:=20Security=20Permit=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20JwtAuthentiactionF?= =?UTF-8?q?ilter=20shouldNotFilter=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/CommentController.java | 7 +++++-- .../devlinkbackend/config/JwtAuthenticationFilter.java | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java index 638748d..863c2f2 100644 --- a/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java +++ b/src/main/java/com/mtvs/devlinkbackend/comment/controller/CommentController.java @@ -41,11 +41,14 @@ public ResponseEntity registComment( } @Operation(summary = "댓글 조회", description = "ID를 사용하여 댓글을 조회합니다.") - @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 조회되었습니다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "댓글이 성공적으로 조회되었습니다."), + @ApiResponse(responseCode = "404", description = "해당 댓글을 찾을 수 없습니다.") + }) @GetMapping("/{commentId}") public ResponseEntity findCommentByCommentId(@PathVariable Long commentId) { Comment comment = commentService.findCommentByCommentId(commentId); - return ResponseEntity.ok(comment); + return comment != null ? ResponseEntity.ok(comment) : ResponseEntity.notFound().build(); } @Operation(summary = "요청 ID로 댓글 조회", description = "특정 요청에 연관된 모든 댓글을 조회합니다.") diff --git a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java index c497b64..d0034d8 100644 --- a/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/mtvs/devlinkbackend/config/JwtAuthenticationFilter.java @@ -78,8 +78,7 @@ protected boolean shouldNotFilter(HttpServletRequest request) { || path.equals("/swagger-ui.html") || path.startsWith("/swagger-resources") || path.startsWith("/webjars") - || path.startsWith("/login") - || path.startsWith("/**"); + || path.startsWith("/api"); } // 쿠키에서 리프레시 토큰을 추출하는 메서드