From 7a7d5b3847cbdb680b8642d23283ba3be9d35469 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Tue, 25 Oct 2022 01:31:06 +0900 Subject: [PATCH 01/11] =?UTF-8?q?#23=20feat:=20ClipBoard=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/ClipBoardController.java | 40 +++++++++++ .../request/clipboard/CreateClipBoardReq.java | 31 ++++++++ .../dto/response/clipboard/ClipBoardRes.java | 72 +++++++++++++++++++ .../yogit/server/board/entity/ClipBoard.java | 22 ++++-- .../board/repository/ClipBoardRepository.java | 10 +++ .../service/clipboard/ClipBoardService.java | 10 +++ .../clipboard/ClipBoardServiceImpl.java | 43 +++++++++++ 7 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java create mode 100644 server/src/main/java/com/yogit/server/board/dto/request/clipboard/CreateClipBoardReq.java create mode 100644 server/src/main/java/com/yogit/server/board/dto/response/clipboard/ClipBoardRes.java create mode 100644 server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java create mode 100644 server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java create mode 100644 server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java diff --git a/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java b/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java new file mode 100644 index 0000000..200b9ae --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java @@ -0,0 +1,40 @@ +package com.yogit.server.board.controller; + +import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; +import com.yogit.server.board.service.clipboard.ClipBoardService; +import com.yogit.server.global.dto.ApplicationResponse; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +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; + +@Slf4j +@RestController +@RequiredArgsConstructor // private final DI의존주입 +@RequestMapping("/clipboards") +public class ClipBoardController { + + private final ClipBoardService clipBoardService; + + /** + * 클립보드 등록 + * @author 토마스 + */ + @ApiOperation(value = "클립보드 등록", notes = "클리보드 제목과, 내용으로 등록 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @PostMapping("") + public ApplicationResponse createClipBoard(@RequestBody @Validated CreateClipBoardReq createClipBoardReq){ + return clipBoardService.createClipBoard(createClipBoardReq); + } +} diff --git a/server/src/main/java/com/yogit/server/board/dto/request/clipboard/CreateClipBoardReq.java b/server/src/main/java/com/yogit/server/board/dto/request/clipboard/CreateClipBoardReq.java new file mode 100644 index 0000000..6aca18d --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/request/clipboard/CreateClipBoardReq.java @@ -0,0 +1,31 @@ +package com.yogit.server.board.dto.request.clipboard; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Data +@NoArgsConstructor +public class CreateClipBoardReq { + + @ApiModelProperty(example = "1") + @ApiParam(value = "Board ID", required = true) + private Long boardId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID", required = true) + private Long userId; + + @ApiModelProperty(example = "질문이 있습니다.") + @ApiParam(value = "클립보드 제목", required = true) + @NotBlank + private String title; + + @ApiModelProperty(example = "경복궁역 몇 번 출구인가요?") + @ApiParam(value = "클립보드 상세 내용", required = false) + @NotBlank + private String content; +} diff --git a/server/src/main/java/com/yogit/server/board/dto/response/clipboard/ClipBoardRes.java b/server/src/main/java/com/yogit/server/board/dto/response/clipboard/ClipBoardRes.java new file mode 100644 index 0000000..79d5376 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/response/clipboard/ClipBoardRes.java @@ -0,0 +1,72 @@ +package com.yogit.server.board.dto.response.clipboard; + + +import com.yogit.server.board.entity.ClipBoard; +import com.yogit.server.config.domain.BaseStatus; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class ClipBoardRes { + + @ApiModelProperty(example = "1") + @ApiParam(value = "ClipBoard ID") + private Long clipBoardId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID") + private Long userId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "Board ID") + private Long boardId; + + @ApiModelProperty(example = "질문이 있습니다.") + @ApiParam(value = "클립보드 제목") + private String title; + + @ApiModelProperty(example = "경복궁역 몇 번 출구인가요?") + @ApiParam(value = "클립보드 상세 내용") + private String content; + + @ApiModelProperty(example = "ACTIVE") + @ApiParam(value = "객체 상태") + private BaseStatus status; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "생성 시각") + private String createdAt; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "마지막 업데이트 시각") + private String updatedAt; + + public static ClipBoardRes toDto(ClipBoard clipBoard){ + return ClipBoardRes.builder() + .clipBoardId(clipBoard.getId()) + .userId(clipBoard.getUser().getId()) + .boardId(clipBoard.getBoard().getId()) + .title(clipBoard.getTitle()) + .content(clipBoard.getContent()) + .status(clipBoard.getStatus()) + .createdAt(clipBoard.getCreatedAt()) + .updatedAt(clipBoard.getUpdatedAt()) + .build(); + } + + @Builder + public ClipBoardRes(Long clipBoardId, Long userId, Long boardId, String title, String content, BaseStatus status, String createdAt, String updatedAt) { + this.clipBoardId = clipBoardId; + this.userId = userId; + this.boardId = boardId; + this.title = title; + this.content = content; + this.status = status; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/server/src/main/java/com/yogit/server/board/entity/ClipBoard.java b/server/src/main/java/com/yogit/server/board/entity/ClipBoard.java index 9bc46b8..3995a42 100644 --- a/server/src/main/java/com/yogit/server/board/entity/ClipBoard.java +++ b/server/src/main/java/com/yogit/server/board/entity/ClipBoard.java @@ -1,8 +1,10 @@ package com.yogit.server.board.entity; +import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; import com.yogit.server.config.domain.BaseEntity; import com.yogit.server.user.entity.User; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,17 +20,25 @@ public class ClipBoard extends BaseEntity { @Column(name = "clip_board_id") private Long id; - private String title; - private String content; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "board_id") private Board board; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; - @OneToMany(mappedBy = "clipBoard") private List comments; + + private String title; + + private String content; + + public ClipBoard(CreateClipBoardReq dto, User user, Board board) { + this.user = user; + this.board = board; + this.title = dto.getTitle(); + this.content = dto.getContent(); + } } diff --git a/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java b/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java new file mode 100644 index 0000000..c156b92 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java @@ -0,0 +1,10 @@ +package com.yogit.server.board.repository; + + +import com.yogit.server.board.entity.ClipBoard; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ClipBoardRepository extends JpaRepository { + + +} diff --git a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java new file mode 100644 index 0000000..cdc459c --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java @@ -0,0 +1,10 @@ +package com.yogit.server.board.service.clipboard; + +import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; +import com.yogit.server.global.dto.ApplicationResponse; + +public interface ClipBoardService { + + ApplicationResponse createClipBoard(CreateClipBoardReq createClipBoardReq); +} diff --git a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java new file mode 100644 index 0000000..612f894 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java @@ -0,0 +1,43 @@ +package com.yogit.server.board.service.clipboard; + +import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; +import com.yogit.server.board.entity.Board; +import com.yogit.server.board.entity.ClipBoard; +import com.yogit.server.board.exception.NotFoundBoardException; +import com.yogit.server.board.repository.BoardRepository; +import com.yogit.server.board.repository.ClipBoardRepository; +import com.yogit.server.global.dto.ApplicationResponse; +import com.yogit.server.user.entity.User; +import com.yogit.server.user.exception.NotFoundUserException; +import com.yogit.server.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class ClipBoardServiceImpl implements ClipBoardService{ + + private final ClipBoardRepository clipBoardRepository; + private final UserRepository userRepository; + private final BoardRepository boardRepository; + + @Transactional(readOnly = false) + @Override + public ApplicationResponse createClipBoard(CreateClipBoardReq dto){ + + User user = userRepository.findById(dto.getUserId()) + .orElseThrow(() -> new NotFoundUserException()); + + Board board = boardRepository.findBoardById(dto.getBoardId()) + .orElseThrow(() -> new NotFoundBoardException()); + + // ClipBoard객체 생성 + ClipBoard clipBoard = new ClipBoard(dto, user, board); + ClipBoard savedClipBoard = clipBoardRepository.save(clipBoard); // 생성 요청 + ClipBoardRes clipBoardRes = ClipBoardRes.toDto(savedClipBoard);// resDto로 변환 + return ApplicationResponse.create("클립보드 생성에 성공하였습니다.", clipBoardRes); + } +} From 238716a2171c8d31e420518c3e178769229dfd0b Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Tue, 25 Oct 2022 15:05:15 +0900 Subject: [PATCH 02/11] =?UTF-8?q?#23=20feat:=20ClipBoard=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=EA=B5=AC=ED=98=84,=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EB=B0=8F=20res=EC=97=90=20=ED=8F=AC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/ClipBoardController.java | 17 ++++ .../request/clipboard/GetClipBoardReq.java | 23 ++++++ .../response/clipboard/GetClipBoardRes.java | 82 +++++++++++++++++++ .../dto/response/comment/CommentRes.java | 60 ++++++++++++++ .../clipboard/ClipBoardException.java | 12 +++ .../clipboard/ClipBoardExceptionList.java | 18 ++++ .../clipboard/NotFoundClipBoardException.java | 8 ++ .../exception/comment/CommentException.java | 11 +++ .../comment/CommentExceptionList.java | 18 ++++ .../comment/NotFoundCommentException.java | 8 ++ .../board/repository/ClipBoardRepository.java | 7 ++ .../board/repository/CommentRepository.java | 15 ++++ .../service/clipboard/ClipBoardService.java | 4 + .../clipboard/ClipBoardServiceImpl.java | 34 ++++++++ 14 files changed, 317 insertions(+) create mode 100644 server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetClipBoardReq.java create mode 100644 server/src/main/java/com/yogit/server/board/dto/response/clipboard/GetClipBoardRes.java create mode 100644 server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardException.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardExceptionList.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/clipboard/NotFoundClipBoardException.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/comment/CommentException.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/comment/NotFoundCommentException.java create mode 100644 server/src/main/java/com/yogit/server/board/repository/CommentRepository.java diff --git a/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java b/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java index 200b9ae..078b72e 100644 --- a/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java +++ b/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java @@ -1,7 +1,9 @@ package com.yogit.server.board.controller; import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.request.clipboard.GetClipBoardReq; import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; +import com.yogit.server.board.dto.response.clipboard.GetClipBoardRes; import com.yogit.server.board.service.clipboard.ClipBoardService; import com.yogit.server.global.dto.ApplicationResponse; import io.swagger.annotations.ApiOperation; @@ -37,4 +39,19 @@ public class ClipBoardController { public ApplicationResponse createClipBoard(@RequestBody @Validated CreateClipBoardReq createClipBoardReq){ return clipBoardService.createClipBoard(createClipBoardReq); } + + /** + * 클립보드 단건 조회 + * @author 토마스 + */ + @ApiOperation(value = "클립보드 단건 조회", notes = "클립보드 ID로 단건 조회 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @PostMapping("/get") + public ApplicationResponse findClipBoard(@RequestBody @Validated GetClipBoardReq getClipBoardReq){ + return clipBoardService.findClipBoard(getClipBoardReq); + } } diff --git a/server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetClipBoardReq.java b/server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetClipBoardReq.java new file mode 100644 index 0000000..42376a4 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetClipBoardReq.java @@ -0,0 +1,23 @@ +package com.yogit.server.board.dto.request.clipboard; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class GetClipBoardReq { + + @ApiModelProperty(example = "1") + @ApiParam(value = "clipBoard ID", required = true) + private Long clipBoardId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "Board ID", required = true) + private Long boardId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID", required = true) + private Long userId; +} diff --git a/server/src/main/java/com/yogit/server/board/dto/response/clipboard/GetClipBoardRes.java b/server/src/main/java/com/yogit/server/board/dto/response/clipboard/GetClipBoardRes.java new file mode 100644 index 0000000..559ad05 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/response/clipboard/GetClipBoardRes.java @@ -0,0 +1,82 @@ +package com.yogit.server.board.dto.response.clipboard; + +import com.yogit.server.board.dto.response.comment.CommentRes; +import com.yogit.server.board.entity.ClipBoard; +import com.yogit.server.board.entity.Comment; +import com.yogit.server.config.domain.BaseStatus; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +public class GetClipBoardRes { + + @ApiModelProperty(example = "1") + @ApiParam(value = "ClipBoard ID") + private Long clipBoardId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID") + private Long userId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "Board ID") + private Long boardId; + + @ApiModelProperty(example = "질문이 있습니다.") + @ApiParam(value = "클립보드 제목") + private String title; + + @ApiModelProperty(example = "경복궁역 몇 번 출구인가요?") + @ApiParam(value = "클립보드 상세 내용") + private String content; + + @ApiModelProperty(example = "댓글1") + @ApiParam(value = "코멘트들") + private List commentResList; + + @ApiModelProperty(example = "ACTIVE") + @ApiParam(value = "객체 상태") + private BaseStatus status; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "생성 시각") + private String createdAt; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "마지막 업데이트 시각") + private String updatedAt; + + public static GetClipBoardRes toDto(ClipBoard clipBoard, List commentResList){ + return GetClipBoardRes.builder() + .clipBoardId(clipBoard.getId()) + .userId(clipBoard.getUser().getId()) + .boardId(clipBoard.getBoard().getId()) + .title(clipBoard.getTitle()) + .content(clipBoard.getContent()) + .commentResList(commentResList) + .status(clipBoard.getStatus()) + .createdAt(clipBoard.getCreatedAt()) + .updatedAt(clipBoard.getUpdatedAt()) + .build(); + } + + @Builder + + public GetClipBoardRes(Long clipBoardId, Long userId, Long boardId, String title, String content, List commentResList, BaseStatus status, String createdAt, String updatedAt) { + this.clipBoardId = clipBoardId; + this.userId = userId; + this.boardId = boardId; + this.title = title; + this.content = content; + this.commentResList = commentResList; + this.status = status; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java b/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java new file mode 100644 index 0000000..cb75e1e --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java @@ -0,0 +1,60 @@ +package com.yogit.server.board.dto.response.comment; + +import com.yogit.server.board.entity.ClipBoard; +import com.yogit.server.board.entity.Comment; +import com.yogit.server.user.entity.User; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class CommentRes { + + @ApiModelProperty(example = "1") + @ApiParam(value = "Comment ID") + private Long commentId; + + @ApiModelProperty(example = "경복궁역 몇 번 출구인가요?") + @ApiParam(value = "클립보드 상세 내용") + private String content; + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID") + private Long userId; + + @ApiModelProperty(example = "Park jun") + @ApiParam(value = "유저 이름") + private String userName; + + @ApiModelProperty(example = "추가 예정") + @ApiParam(value = "유저 프로필 이미지") + private String profileImg; + + @ApiModelProperty(example = "1") + @ApiParam(value = "ClipBoard ID") + private Long clipBoardId; + + public static CommentRes toDto(Comment comment){ + return CommentRes.builder() + .commentId(comment.getId()) + .content(comment.getContent()) + .userId(comment.getUser().getId()) + .userName(comment.getUser().getName()) + .profileImg(comment.getUser().getProfileImg()) + .clipBoardId(comment.getClipBoard().getId()) + .build(); + } + + @Builder + public CommentRes(Long commentId, String content, Long userId, String userName, String profileImg, Long clipBoardId) { + this.commentId = commentId; + this.content = content; + this.userId = userId; + this.userName = userName; + this.profileImg = profileImg; + this.clipBoardId = clipBoardId; + } +} diff --git a/server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardException.java b/server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardException.java new file mode 100644 index 0000000..0788fe3 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardException.java @@ -0,0 +1,12 @@ +package com.yogit.server.board.exception.clipboard; + + +import com.yogit.server.global.exception.ApplicationException; +import org.springframework.http.HttpStatus; + +public class ClipBoardException extends ApplicationException { + + protected ClipBoardException(String errorCode, HttpStatus httpStatus, String message) { + super(errorCode, httpStatus, message); + } +} diff --git a/server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardExceptionList.java b/server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardExceptionList.java new file mode 100644 index 0000000..dad5f8d --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/clipboard/ClipBoardExceptionList.java @@ -0,0 +1,18 @@ +package com.yogit.server.board.exception.clipboard; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@Getter +@RequiredArgsConstructor +public enum ClipBoardExceptionList { + + NOT_FOUND_CLIP_BOARD("CB0001", NOT_FOUND, "존재하지 않는 ClipBoard아이디입니다."); + + private final String CODE; + private final HttpStatus HTTPSTATUS; + private final String MESSAGE; +} diff --git a/server/src/main/java/com/yogit/server/board/exception/clipboard/NotFoundClipBoardException.java b/server/src/main/java/com/yogit/server/board/exception/clipboard/NotFoundClipBoardException.java new file mode 100644 index 0000000..e91dfb4 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/clipboard/NotFoundClipBoardException.java @@ -0,0 +1,8 @@ +package com.yogit.server.board.exception.clipboard; + +public class NotFoundClipBoardException extends ClipBoardException{ + + public NotFoundClipBoardException(){ + super(ClipBoardExceptionList.NOT_FOUND_CLIP_BOARD.getCODE(), ClipBoardExceptionList.NOT_FOUND_CLIP_BOARD.getHTTPSTATUS(), ClipBoardExceptionList.NOT_FOUND_CLIP_BOARD.getMESSAGE()); + } +} diff --git a/server/src/main/java/com/yogit/server/board/exception/comment/CommentException.java b/server/src/main/java/com/yogit/server/board/exception/comment/CommentException.java new file mode 100644 index 0000000..d67e8b7 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/comment/CommentException.java @@ -0,0 +1,11 @@ +package com.yogit.server.board.exception.comment; + +import com.yogit.server.global.exception.ApplicationException; +import org.springframework.http.HttpStatus; + +public class CommentException extends ApplicationException { + + protected CommentException(String errorCode, HttpStatus httpStatus, String message) { + super(errorCode, httpStatus, message); + } +} diff --git a/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java b/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java new file mode 100644 index 0000000..a1dc65a --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java @@ -0,0 +1,18 @@ +package com.yogit.server.board.exception.comment; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.NOT_FOUND; + +@Getter +@RequiredArgsConstructor +public enum CommentExceptionList { + + NOT_FOUND_COMMENT("CM0001", NOT_FOUND, "존재하지 않는 Comment입니다."); + + private final String CODE; + private final HttpStatus HTTPSTATUS; + private final String MESSAGE; +} diff --git a/server/src/main/java/com/yogit/server/board/exception/comment/NotFoundCommentException.java b/server/src/main/java/com/yogit/server/board/exception/comment/NotFoundCommentException.java new file mode 100644 index 0000000..b46b283 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/comment/NotFoundCommentException.java @@ -0,0 +1,8 @@ +package com.yogit.server.board.exception.comment; + +public class NotFoundCommentException extends CommentException{ + + public NotFoundCommentException(){ + super(CommentExceptionList.NOT_FOUND_COMMENT.getCODE(), CommentExceptionList.NOT_FOUND_COMMENT.getHTTPSTATUS(), CommentExceptionList.NOT_FOUND_COMMENT.getMESSAGE()); + } +} diff --git a/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java b/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java index c156b92..ff63624 100644 --- a/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java +++ b/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java @@ -3,8 +3,15 @@ import com.yogit.server.board.entity.ClipBoard; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; public interface ClipBoardRepository extends JpaRepository { + @Query("select cb from ClipBoard cb where cb.id = :clipBoardId and cb.status = 'ACTIVE'") + Optional findClipBoardById(@Param("clipBoardId") Long clipBoardId); + } diff --git a/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java b/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java new file mode 100644 index 0000000..6e1b4f1 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java @@ -0,0 +1,15 @@ +package com.yogit.server.board.repository; + +import com.yogit.server.board.entity.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Optional; + +public interface CommentRepository extends JpaRepository { + + @Query("select cm from Comment cm where cm.clipBoard.id = :clipBoardId and cm.status = 'ACTIVE'") + List findAllCommentsByClipBoardId(@Param("clipBoardId") Long clipBoardId); +} diff --git a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java index cdc459c..9cbfcb6 100644 --- a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java +++ b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java @@ -1,10 +1,14 @@ package com.yogit.server.board.service.clipboard; import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.request.clipboard.GetClipBoardReq; import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; +import com.yogit.server.board.dto.response.clipboard.GetClipBoardRes; import com.yogit.server.global.dto.ApplicationResponse; public interface ClipBoardService { ApplicationResponse createClipBoard(CreateClipBoardReq createClipBoardReq); + + ApplicationResponse findClipBoard(GetClipBoardReq getClipBoardReq); } diff --git a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java index 612f894..6a45e2e 100644 --- a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java +++ b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java @@ -1,12 +1,19 @@ package com.yogit.server.board.service.clipboard; import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.request.clipboard.GetClipBoardReq; import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; +import com.yogit.server.board.dto.response.clipboard.GetClipBoardRes; +import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.board.entity.Board; import com.yogit.server.board.entity.ClipBoard; +import com.yogit.server.board.entity.Comment; import com.yogit.server.board.exception.NotFoundBoardException; +import com.yogit.server.board.exception.clipboard.NotFoundClipBoardException; +import com.yogit.server.board.exception.comment.NotFoundCommentException; import com.yogit.server.board.repository.BoardRepository; import com.yogit.server.board.repository.ClipBoardRepository; +import com.yogit.server.board.repository.CommentRepository; import com.yogit.server.global.dto.ApplicationResponse; import com.yogit.server.user.entity.User; import com.yogit.server.user.exception.NotFoundUserException; @@ -15,6 +22,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -23,6 +33,7 @@ public class ClipBoardServiceImpl implements ClipBoardService{ private final ClipBoardRepository clipBoardRepository; private final UserRepository userRepository; private final BoardRepository boardRepository; + private final CommentRepository commentRepository; @Transactional(readOnly = false) @Override @@ -40,4 +51,27 @@ public ApplicationResponse createClipBoard(CreateClipBoardReq dto) ClipBoardRes clipBoardRes = ClipBoardRes.toDto(savedClipBoard);// resDto로 변환 return ApplicationResponse.create("클립보드 생성에 성공하였습니다.", clipBoardRes); } + + + @Transactional(readOnly = true) + @Override + public ApplicationResponse findClipBoard(GetClipBoardReq dto){ + + User user = userRepository.findById(dto.getUserId()) + .orElseThrow(() -> new NotFoundUserException()); + + Board board = boardRepository.findBoardById(dto.getBoardId()) + .orElseThrow(() -> new NotFoundBoardException()); + + ClipBoard clipBoard = clipBoardRepository.findClipBoardById(dto.getClipBoardId()) + .orElseThrow(() -> new NotFoundClipBoardException()); + + List comments = commentRepository.findAllCommentsByClipBoardId(clipBoard.getId()).stream() + .map(comment -> CommentRes.toDto(comment)) + .collect(Collectors.toList()); + + // 코멘트 추가 + GetClipBoardRes getClipBoardRes = GetClipBoardRes.toDto(clipBoard, comments); + return ApplicationResponse.ok(getClipBoardRes); + } } From 4d30eeda5cba986ef64f745d5a7c086c1680702e Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Tue, 25 Oct 2022 15:33:43 +0900 Subject: [PATCH 03/11] =?UTF-8?q?#23=20feat:=20ClipBoard=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84,=20?= =?UTF-8?q?=EC=BD=94=EB=A9=98=ED=8A=B8=20=EA=B0=AF=EC=88=98=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/ClipBoardController.java | 19 +++++++++++++++++++ .../clipboard/GetAllClipBoardsReq.java | 19 +++++++++++++++++++ .../board/repository/ClipBoardRepository.java | 4 +++- .../board/repository/CommentRepository.java | 1 - .../service/clipboard/ClipBoardService.java | 5 +++++ .../clipboard/ClipBoardServiceImpl.java | 19 +++++++++++++++++++ 6 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetAllClipBoardsReq.java diff --git a/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java b/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java index 078b72e..eea2686 100644 --- a/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java +++ b/server/src/main/java/com/yogit/server/board/controller/ClipBoardController.java @@ -1,6 +1,7 @@ package com.yogit.server.board.controller; import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.request.clipboard.GetAllClipBoardsReq; import com.yogit.server.board.dto.request.clipboard.GetClipBoardReq; import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; import com.yogit.server.board.dto.response.clipboard.GetClipBoardRes; @@ -17,6 +18,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @Slf4j @RestController @RequiredArgsConstructor // private final DI의존주입 @@ -54,4 +57,20 @@ public ApplicationResponse createClipBoard(@RequestBody @Validated public ApplicationResponse findClipBoard(@RequestBody @Validated GetClipBoardReq getClipBoardReq){ return clipBoardService.findClipBoard(getClipBoardReq); } + + + /** + * 클립보드 전체 조회 + * @author 토마스 + */ + @ApiOperation(value = "클립보드 전체 조회", notes = "Board ID로 클립보드 전체 조회 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @PostMapping("/get/all") + public ApplicationResponse> findAllClipBoards(@RequestBody @Validated GetAllClipBoardsReq getAllClipBoardsReq){ + return clipBoardService.findAllClipBoards(getAllClipBoardsReq); + } } diff --git a/server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetAllClipBoardsReq.java b/server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetAllClipBoardsReq.java new file mode 100644 index 0000000..35ace69 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/request/clipboard/GetAllClipBoardsReq.java @@ -0,0 +1,19 @@ +package com.yogit.server.board.dto.request.clipboard; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class GetAllClipBoardsReq { + + @ApiModelProperty(example = "1") + @ApiParam(value = "Board ID", required = true) + private Long boardId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID", required = true) + private Long userId; +} diff --git a/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java b/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java index ff63624..de620ba 100644 --- a/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java +++ b/server/src/main/java/com/yogit/server/board/repository/ClipBoardRepository.java @@ -6,6 +6,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.List; import java.util.Optional; public interface ClipBoardRepository extends JpaRepository { @@ -13,5 +14,6 @@ public interface ClipBoardRepository extends JpaRepository { @Query("select cb from ClipBoard cb where cb.id = :clipBoardId and cb.status = 'ACTIVE'") Optional findClipBoardById(@Param("clipBoardId") Long clipBoardId); - + @Query("select cb from ClipBoard cb where cb.board.id = :boardId and cb.status = 'ACTIVE'") + List findAllByBoardId(@Param("boardId") Long boardId); } diff --git a/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java b/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java index 6e1b4f1..9ced032 100644 --- a/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java +++ b/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java @@ -6,7 +6,6 @@ import org.springframework.data.repository.query.Param; import java.util.List; -import java.util.Optional; public interface CommentRepository extends JpaRepository { diff --git a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java index 9cbfcb6..9bce9de 100644 --- a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java +++ b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardService.java @@ -1,14 +1,19 @@ package com.yogit.server.board.service.clipboard; import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.request.clipboard.GetAllClipBoardsReq; import com.yogit.server.board.dto.request.clipboard.GetClipBoardReq; import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; import com.yogit.server.board.dto.response.clipboard.GetClipBoardRes; import com.yogit.server.global.dto.ApplicationResponse; +import java.util.List; + public interface ClipBoardService { ApplicationResponse createClipBoard(CreateClipBoardReq createClipBoardReq); ApplicationResponse findClipBoard(GetClipBoardReq getClipBoardReq); + + ApplicationResponse> findAllClipBoards(GetAllClipBoardsReq getAllClipBoardsReq); } diff --git a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java index 6a45e2e..37aa717 100644 --- a/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java +++ b/server/src/main/java/com/yogit/server/board/service/clipboard/ClipBoardServiceImpl.java @@ -1,6 +1,7 @@ package com.yogit.server.board.service.clipboard; import com.yogit.server.board.dto.request.clipboard.CreateClipBoardReq; +import com.yogit.server.board.dto.request.clipboard.GetAllClipBoardsReq; import com.yogit.server.board.dto.request.clipboard.GetClipBoardReq; import com.yogit.server.board.dto.response.clipboard.ClipBoardRes; import com.yogit.server.board.dto.response.clipboard.GetClipBoardRes; @@ -74,4 +75,22 @@ public ApplicationResponse findClipBoard(GetClipBoardReq dto){ GetClipBoardRes getClipBoardRes = GetClipBoardRes.toDto(clipBoard, comments); return ApplicationResponse.ok(getClipBoardRes); } + + + @Transactional(readOnly = true) + @Override + public ApplicationResponse> findAllClipBoards(GetAllClipBoardsReq dto){ + + User user = userRepository.findById(dto.getUserId()) + .orElseThrow(() -> new NotFoundUserException()); + + Board board = boardRepository.findBoardById(dto.getBoardId()) + .orElseThrow(() -> new NotFoundBoardException()); + + List clipBoardResList = clipBoardRepository.findAllByBoardId(dto.getBoardId()).stream() + .map(clipBoard -> ClipBoardRes.toDto(clipBoard)) + .collect(Collectors.toList()); + + return ApplicationResponse.ok(clipBoardResList); + } } From 7a374907d0fbf2ac163e784df3d28852e9517088 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 14:52:09 +0900 Subject: [PATCH 04/11] =?UTF-8?q?#27=20feat:=20Comment=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20API=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/CommenrController.java | 40 ++++++++++++++++ .../dto/request/comment/CreateCommentReq.java | 26 +++++++++++ .../yogit/server/board/entity/Comment.java | 11 +++++ .../board/service/comment/CommentService.java | 10 ++++ .../service/comment/CommentServiceImpl.java | 46 +++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 server/src/main/java/com/yogit/server/board/controller/CommenrController.java create mode 100644 server/src/main/java/com/yogit/server/board/dto/request/comment/CreateCommentReq.java create mode 100644 server/src/main/java/com/yogit/server/board/service/comment/CommentService.java create mode 100644 server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java diff --git a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java new file mode 100644 index 0000000..0ebe4c8 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java @@ -0,0 +1,40 @@ +package com.yogit.server.board.controller; + +import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.response.comment.CommentRes; +import com.yogit.server.board.service.comment.CommentService; +import com.yogit.server.global.dto.ApplicationResponse; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +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; + +@Slf4j +@RestController +@RequiredArgsConstructor // private final DI의존주입 +@RequestMapping("/comments") +public class CommenrController { + + private final CommentService commentService; + /** + * 코멘트 등록 + * @author 토마스 + */ + @ApiOperation(value = "코멘트 등록", notes = "코멘트 내용을 입력해 등록 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code= 404, message = "존재하지 않는 클립보드입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @PostMapping("") + public ApplicationResponse createComment(@RequestBody @Validated CreateCommentReq createCommentReq){ + return commentService.createComment(createCommentReq); + } +} diff --git a/server/src/main/java/com/yogit/server/board/dto/request/comment/CreateCommentReq.java b/server/src/main/java/com/yogit/server/board/dto/request/comment/CreateCommentReq.java new file mode 100644 index 0000000..955ccab --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/request/comment/CreateCommentReq.java @@ -0,0 +1,26 @@ +package com.yogit.server.board.dto.request.comment; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Data +@NoArgsConstructor +public class CreateCommentReq { + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID", required = true) + private Long userId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "ClipBoard ID", required = true) + private Long clipBoardId; + + @ApiModelProperty(example = "경복궁역 몇 번 출구인가요?") + @ApiParam(value = "클립보드 상세 내용", required = true) + @NotBlank + private String content; +} diff --git a/server/src/main/java/com/yogit/server/board/entity/Comment.java b/server/src/main/java/com/yogit/server/board/entity/Comment.java index 8697e55..5930884 100644 --- a/server/src/main/java/com/yogit/server/board/entity/Comment.java +++ b/server/src/main/java/com/yogit/server/board/entity/Comment.java @@ -1,5 +1,6 @@ package com.yogit.server.board.entity; +import com.yogit.server.board.dto.request.comment.CreateCommentReq; import com.yogit.server.config.domain.BaseEntity; import com.yogit.server.user.entity.User; import lombok.AccessLevel; @@ -26,4 +27,14 @@ public class Comment extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "clip_board_id") private ClipBoard clipBoard; + + /* + 연관관계 편의 메소드 + */ + + public Comment(CreateCommentReq dto, User user, ClipBoard clipBoard) { + this.content = dto.getContent(); + this.user = user; + this.clipBoard = clipBoard; + } } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java new file mode 100644 index 0000000..d4b4bf3 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java @@ -0,0 +1,10 @@ +package com.yogit.server.board.service.comment; + +import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.response.comment.CommentRes; +import com.yogit.server.global.dto.ApplicationResponse; + +public interface CommentService { + + ApplicationResponse createComment(CreateCommentReq createCommentReq); +} diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java new file mode 100644 index 0000000..f48e083 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java @@ -0,0 +1,46 @@ +package com.yogit.server.board.service.comment; + +import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.response.comment.CommentRes; +import com.yogit.server.board.entity.ClipBoard; +import com.yogit.server.board.entity.Comment; +import com.yogit.server.board.exception.clipboard.NotFoundClipBoardException; +import com.yogit.server.board.repository.ClipBoardRepository; +import com.yogit.server.board.repository.CommentRepository; +import com.yogit.server.global.dto.ApplicationResponse; +import com.yogit.server.user.entity.User; +import com.yogit.server.user.exception.NotFoundUserException; +import com.yogit.server.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class CommentServiceImpl implements CommentService{ + + private final CommentRepository commentRepository; + private final UserRepository userRepository; + private final ClipBoardRepository clipBoardRepository; + + + + @Transactional(readOnly = false) + @Override + public ApplicationResponse createComment(CreateCommentReq dto){ + + User user = userRepository.findById(dto.getUserId()) + .orElseThrow(() -> new NotFoundUserException()); + + ClipBoard clipBoard = clipBoardRepository.findClipBoardById(dto.getClipBoardId()) + .orElseThrow(() -> new NotFoundClipBoardException()); + + Comment comment = new Comment(dto, user, clipBoard); + Comment savedComment = commentRepository.save(comment); + CommentRes commentRes = CommentRes.toDto(savedComment); + return ApplicationResponse.create("코멘트 생성을 성공했습니다.", commentRes); + } + + +} From 8c227eb9b39c71b288c436e15107c49ed49c8001 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 15:41:37 +0900 Subject: [PATCH 05/11] =?UTF-8?q?#27=20feat:=20Comment=20ClipBoardId=20?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EC=B2=B4=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/CommenrController.java | 34 +++++++++++++++---- .../board/service/comment/CommentService.java | 4 +++ .../service/comment/CommentServiceImpl.java | 18 ++++++++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java index 0ebe4c8..09fba83 100644 --- a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java +++ b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java @@ -4,16 +4,15 @@ import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.board.service.comment.CommentService; import com.yogit.server.global.dto.ApplicationResponse; -import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiResponse; -import io.swagger.annotations.ApiResponses; +import com.yogit.server.user.entity.Gender; +import com.yogit.server.user.entity.Nationality; +import io.swagger.annotations.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; -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 org.springframework.web.bind.annotation.*; + +import java.util.List; @Slf4j @RestController @@ -37,4 +36,25 @@ public class CommenrController { public ApplicationResponse createComment(@RequestBody @Validated CreateCommentReq createCommentReq){ return commentService.createComment(createCommentReq); } + + + /** + * 코멘트 전체 조회 + * @author 토마스 + */ + @ApiOperation(value = "코멘트 전체 조회", notes = "클립보드 아이디를 입력해 코멘트 전체조회 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code= 404, message = "존재하지 않는 클립보드입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", required = true, dataTypeClass = Long.class, example = "1"), + @ApiImplicitParam(name = "clipBoardId", required = true, dataTypeClass = Long.class, example = "1") + }) + @GetMapping("/clipboard/{clipBoardId}/user/{userId}") + public ApplicationResponse> findAllComments(@PathVariable("clipBoardId") Long clipBoardId, @PathVariable("userId") Long userId){ + return commentService.findAllComments(clipBoardId, userId); + } } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java index d4b4bf3..e4a3cc2 100644 --- a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java @@ -4,7 +4,11 @@ import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.global.dto.ApplicationResponse; +import java.util.List; + public interface CommentService { ApplicationResponse createComment(CreateCommentReq createCommentReq); + + ApplicationResponse> findAllComments(Long clipBoardId, Long userId); } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java index f48e083..f0feafa 100644 --- a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java @@ -15,6 +15,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service @Transactional(readOnly = true) @RequiredArgsConstructor @@ -42,5 +45,20 @@ public ApplicationResponse createComment(CreateCommentReq dto){ return ApplicationResponse.create("코멘트 생성을 성공했습니다.", commentRes); } + @Transactional(readOnly = true) + @Override + public ApplicationResponse> findAllComments(Long clipBoardId, Long userId){ + + User user = userRepository.findById(userId) + .orElseThrow(() -> new NotFoundUserException()); + + ClipBoard clipBoard = clipBoardRepository.findClipBoardById(clipBoardId) + .orElseThrow(() -> new NotFoundClipBoardException()); + + List commentResList = commentRepository.findAllCommentsByClipBoardId(clipBoardId).stream() + .map(comment -> CommentRes.toDto(comment)) + .collect(Collectors.toList()); + return ApplicationResponse.ok(commentResList); + } } From 1bb8a5f7d756c549febf3fce11907dfc4201d76b Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 17:50:20 +0900 Subject: [PATCH 06/11] =?UTF-8?q?#27=20feat:=20Comment=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20API=20=EA=B5=AC=ED=98=84,=20status=EB=A5=BC=20INACT?= =?UTF-8?q?IVE=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/CommenrController.java | 23 +++++++++ .../dto/request/comment/DeleteCommentReq.java | 15 ++++++ .../response/comment/DeleteCommentRes.java | 50 +++++++++++++++++++ .../yogit/server/board/entity/Comment.java | 5 ++ .../comment/CommentExceptionList.java | 3 +- .../comment/NotHostOfCommentException.java | 9 ++++ .../board/repository/CommentRepository.java | 4 ++ .../board/service/comment/CommentService.java | 4 ++ .../service/comment/CommentServiceImpl.java | 25 ++++++++++ 9 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/com/yogit/server/board/dto/request/comment/DeleteCommentReq.java create mode 100644 server/src/main/java/com/yogit/server/board/dto/response/comment/DeleteCommentRes.java create mode 100644 server/src/main/java/com/yogit/server/board/exception/comment/NotHostOfCommentException.java diff --git a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java index 09fba83..51fee92 100644 --- a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java +++ b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java @@ -1,7 +1,9 @@ package com.yogit.server.board.controller; import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.request.comment.DeleteCommentReq; import com.yogit.server.board.dto.response.comment.CommentRes; +import com.yogit.server.board.dto.response.comment.DeleteCommentRes; import com.yogit.server.board.service.comment.CommentService; import com.yogit.server.global.dto.ApplicationResponse; import com.yogit.server.user.entity.Gender; @@ -57,4 +59,25 @@ public ApplicationResponse createComment(@RequestBody @Validated Cre public ApplicationResponse> findAllComments(@PathVariable("clipBoardId") Long clipBoardId, @PathVariable("userId") Long userId){ return commentService.findAllComments(clipBoardId, userId); } + + + /** + * 코멘트 삭제 + * @author 토마스 + */ + @ApiOperation(value = "코멘트 삭제", notes = "코멘트 아이디를 입력해 코멘트 삭제 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code= 404, message = "존재하지 않는 클립보드입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", required = true, dataTypeClass = Long.class, example = "1"), + @ApiImplicitParam(name = "clipBoardId", required = true, dataTypeClass = Long.class, example = "1") + }) + @PatchMapping("/{commentId}") + public ApplicationResponse deleteComment(@PathVariable("commentId") Long commentId, @RequestBody @Validated DeleteCommentReq deleteCommentReq){ + return commentService.deleteComment(deleteCommentReq, commentId); + } } diff --git a/server/src/main/java/com/yogit/server/board/dto/request/comment/DeleteCommentReq.java b/server/src/main/java/com/yogit/server/board/dto/request/comment/DeleteCommentReq.java new file mode 100644 index 0000000..3a5cb5d --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/request/comment/DeleteCommentReq.java @@ -0,0 +1,15 @@ +package com.yogit.server.board.dto.request.comment; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class DeleteCommentReq { + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID", required = true) + private Long userId; +} diff --git a/server/src/main/java/com/yogit/server/board/dto/response/comment/DeleteCommentRes.java b/server/src/main/java/com/yogit/server/board/dto/response/comment/DeleteCommentRes.java new file mode 100644 index 0000000..2a49e46 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/response/comment/DeleteCommentRes.java @@ -0,0 +1,50 @@ +package com.yogit.server.board.dto.response.comment; + +import com.yogit.server.board.entity.Comment; +import com.yogit.server.config.domain.BaseStatus; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class DeleteCommentRes { + + @ApiModelProperty(example = "1") + @ApiParam(value = "코멘트 ID") + private Long commentId; + + @ApiModelProperty(example = "ACTIVE") + @ApiParam(value = "객체 상태") + private BaseStatus status; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "생성 시각") + private String createdAt; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "마지막 업데이트 시각") + private String updatedAt; + + /* + 연관관계 편의 메서드 + */ + public static DeleteCommentRes toDto(Comment comment){ + return DeleteCommentRes.builder() + .commentId(comment.getId()) + .status(comment.getStatus()) + .createdAt(comment.getCreatedAt()) + .updatedAt(comment.getUpdatedAt()) + .build(); + } + + @Builder + public DeleteCommentRes(Long commentId, BaseStatus status, String createdAt, String updatedAt) { + this.commentId = commentId; + this.status = status; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } +} diff --git a/server/src/main/java/com/yogit/server/board/entity/Comment.java b/server/src/main/java/com/yogit/server/board/entity/Comment.java index 5930884..9e315c1 100644 --- a/server/src/main/java/com/yogit/server/board/entity/Comment.java +++ b/server/src/main/java/com/yogit/server/board/entity/Comment.java @@ -2,6 +2,7 @@ import com.yogit.server.board.dto.request.comment.CreateCommentReq; import com.yogit.server.config.domain.BaseEntity; +import com.yogit.server.config.domain.BaseStatus; import com.yogit.server.user.entity.User; import lombok.AccessLevel; import lombok.Getter; @@ -37,4 +38,8 @@ public Comment(CreateCommentReq dto, User user, ClipBoard clipBoard) { this.user = user; this.clipBoard = clipBoard; } + + public void deleteComment(){ + this.setStatus(BaseStatus.INACTIVE); + } } diff --git a/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java b/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java index a1dc65a..3a96ef0 100644 --- a/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java +++ b/server/src/main/java/com/yogit/server/board/exception/comment/CommentExceptionList.java @@ -10,7 +10,8 @@ @RequiredArgsConstructor public enum CommentExceptionList { - NOT_FOUND_COMMENT("CM0001", NOT_FOUND, "존재하지 않는 Comment입니다."); + NOT_FOUND_COMMENT("CM0001", NOT_FOUND, "존재하지 않는 Comment입니다."), + NOT_HOST_OF_COMMENT("CM0002", HttpStatus.BAD_REQUEST, "요청한 유저가 코멘트의 호스트가 아닙니다."); private final String CODE; private final HttpStatus HTTPSTATUS; diff --git a/server/src/main/java/com/yogit/server/board/exception/comment/NotHostOfCommentException.java b/server/src/main/java/com/yogit/server/board/exception/comment/NotHostOfCommentException.java new file mode 100644 index 0000000..3a70299 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/exception/comment/NotHostOfCommentException.java @@ -0,0 +1,9 @@ +package com.yogit.server.board.exception.comment; + + +public class NotHostOfCommentException extends CommentException{ + public NotHostOfCommentException(){ + super(CommentExceptionList.NOT_FOUND_COMMENT.getCODE(), CommentExceptionList.NOT_FOUND_COMMENT.getHTTPSTATUS(), CommentExceptionList.NOT_FOUND_COMMENT.getMESSAGE()); + } + +} diff --git a/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java b/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java index 9ced032..06da19b 100644 --- a/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java +++ b/server/src/main/java/com/yogit/server/board/repository/CommentRepository.java @@ -6,9 +6,13 @@ import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; public interface CommentRepository extends JpaRepository { @Query("select cm from Comment cm where cm.clipBoard.id = :clipBoardId and cm.status = 'ACTIVE'") List findAllCommentsByClipBoardId(@Param("clipBoardId") Long clipBoardId); + + @Query("select cm from Comment cm where cm.id = :commentId and cm.status='ACTIVE'") + Optional findCommentById(@Param("commentId") Long commentId); } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java index e4a3cc2..fb9ba83 100644 --- a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java @@ -1,7 +1,9 @@ package com.yogit.server.board.service.comment; import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.request.comment.DeleteCommentReq; import com.yogit.server.board.dto.response.comment.CommentRes; +import com.yogit.server.board.dto.response.comment.DeleteCommentRes; import com.yogit.server.global.dto.ApplicationResponse; import java.util.List; @@ -11,4 +13,6 @@ public interface CommentService { ApplicationResponse createComment(CreateCommentReq createCommentReq); ApplicationResponse> findAllComments(Long clipBoardId, Long userId); + + ApplicationResponse deleteComment(DeleteCommentReq deleteCommentReq, Long commentId); } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java index f0feafa..0520287 100644 --- a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java @@ -1,10 +1,14 @@ package com.yogit.server.board.service.comment; import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.request.comment.DeleteCommentReq; +import com.yogit.server.board.dto.response.comment.DeleteCommentRes; import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.board.entity.ClipBoard; import com.yogit.server.board.entity.Comment; import com.yogit.server.board.exception.clipboard.NotFoundClipBoardException; +import com.yogit.server.board.exception.comment.NotFoundCommentException; +import com.yogit.server.board.exception.comment.NotHostOfCommentException; import com.yogit.server.board.repository.ClipBoardRepository; import com.yogit.server.board.repository.CommentRepository; import com.yogit.server.global.dto.ApplicationResponse; @@ -61,4 +65,25 @@ public ApplicationResponse> findAllComments(Long clipBoardId, L return ApplicationResponse.ok(commentResList); } + + + @Transactional(readOnly = false) + @Override + public ApplicationResponse deleteComment(DeleteCommentReq dto, Long commentId){ + + User user = userRepository.findById(dto.getUserId()) + .orElseThrow(() -> new NotFoundUserException()); + + Comment comment = commentRepository.findCommentById(commentId) + .orElseThrow(() -> new NotFoundCommentException()); + + //검증: 요청 유저가 코멘트를 생성한 사람인지 + if(!user.getId().equals(comment.getUser().getId())){ + throw new NotHostOfCommentException(); + } + + comment.deleteComment(); + DeleteCommentRes deleteCommentRes = DeleteCommentRes.toDto(comment); + return ApplicationResponse.ok(deleteCommentRes); + } } From c6fc1e804747247ae026d8c35a580401f960c887 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 17:55:34 +0900 Subject: [PATCH 07/11] =?UTF-8?q?#27=20feat:=20CommentRes=EC=97=90=20statu?= =?UTF-8?q?s,=20createdAt,=20updatedAt=20=ED=95=84=EB=93=9C=EA=B0=92=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 --- .../dto/response/comment/CommentRes.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java b/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java index cb75e1e..5b6ff9c 100644 --- a/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java +++ b/server/src/main/java/com/yogit/server/board/dto/response/comment/CommentRes.java @@ -2,6 +2,7 @@ import com.yogit.server.board.entity.ClipBoard; import com.yogit.server.board.entity.Comment; +import com.yogit.server.config.domain.BaseStatus; import com.yogit.server.user.entity.User; import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiParam; @@ -37,6 +38,18 @@ public class CommentRes { @ApiParam(value = "ClipBoard ID") private Long clipBoardId; + @ApiModelProperty(example = "ACTIVE") + @ApiParam(value = "객체 상태") + private BaseStatus status; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "생성 시각") + private String createdAt; + + @ApiModelProperty(example = "2022-07-13 16:29:30") + @ApiParam(value = "마지막 업데이트 시각") + private String updatedAt; + public static CommentRes toDto(Comment comment){ return CommentRes.builder() .commentId(comment.getId()) @@ -45,16 +58,22 @@ public static CommentRes toDto(Comment comment){ .userName(comment.getUser().getName()) .profileImg(comment.getUser().getProfileImg()) .clipBoardId(comment.getClipBoard().getId()) + .status(comment.getStatus()) + .createdAt(comment.getCreatedAt()) + .updatedAt(comment.getUpdatedAt()) .build(); } @Builder - public CommentRes(Long commentId, String content, Long userId, String userName, String profileImg, Long clipBoardId) { + public CommentRes(Long commentId, String content, Long userId, String userName, String profileImg, Long clipBoardId, BaseStatus status, String createdAt, String updatedAt) { this.commentId = commentId; this.content = content; this.userId = userId; this.userName = userName; this.profileImg = profileImg; this.clipBoardId = clipBoardId; + this.status = status; + this.createdAt = createdAt; + this.updatedAt = updatedAt; } } From 17ded3fd44f4e324b97074166e3942fe10af9475 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 18:02:40 +0900 Subject: [PATCH 08/11] =?UTF-8?q?#27=20fix:=20deleteComment=20=EC=9D=98=20?= =?UTF-8?q?@ApiImplicitParam=20commentId=EC=98=88=EC=A0=9C=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 --- .../com/yogit/server/board/controller/CommenrController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java index 51fee92..bf52a7f 100644 --- a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java +++ b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java @@ -74,7 +74,7 @@ public ApplicationResponse> findAllComments(@PathVariable("clip }) @ApiImplicitParams({ @ApiImplicitParam(name = "userId", required = true, dataTypeClass = Long.class, example = "1"), - @ApiImplicitParam(name = "clipBoardId", required = true, dataTypeClass = Long.class, example = "1") + @ApiImplicitParam(name = "commentId", required = true, dataTypeClass = Long.class, example = "1") }) @PatchMapping("/{commentId}") public ApplicationResponse deleteComment(@PathVariable("commentId") Long commentId, @RequestBody @Validated DeleteCommentReq deleteCommentReq){ From e89e93b94c78548a736be29095dd725bade168b3 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 18:09:16 +0900 Subject: [PATCH 09/11] =?UTF-8?q?#27=20feat:=20deleteComment=EC=97=90=20@A?= =?UTF-8?q?piResponse=EB=A1=9C=20=EC=BD=94=EB=A9=98=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=97=90=EB=9F=AC=20=EC=98=88=EC=A0=9C=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 --- .../com/yogit/server/board/controller/CommenrController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java index bf52a7f..a7b4559 100644 --- a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java +++ b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java @@ -70,6 +70,7 @@ public ApplicationResponse> findAllComments(@PathVariable("clip @ApiResponse(code= 201, message = "요청에 성공하였습니다."), @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), @ApiResponse(code= 404, message = "존재하지 않는 클립보드입니다."), + @ApiResponse(code= 404, message = "존재하지 않는 코멘트입니다."), @ApiResponse(code = 4000 , message = "서버 오류입니다.") }) @ApiImplicitParams({ From ebdc9ba6243988193757ca74ac942ad5acb2e053 Mon Sep 17 00:00:00 2001 From: xhaktmchl Date: Thu, 3 Nov 2022 20:28:31 +0900 Subject: [PATCH 10/11] =?UTF-8?q?#27=20feat:=20Comment=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/CommenrController.java | 23 ++++++++++++++++ .../dto/request/comment/PatchCommentReq.java | 26 +++++++++++++++++++ .../yogit/server/board/entity/Comment.java | 5 ++++ .../board/service/comment/CommentService.java | 3 +++ .../service/comment/CommentServiceImpl.java | 22 ++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 server/src/main/java/com/yogit/server/board/dto/request/comment/PatchCommentReq.java diff --git a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java index a7b4559..456db49 100644 --- a/server/src/main/java/com/yogit/server/board/controller/CommenrController.java +++ b/server/src/main/java/com/yogit/server/board/controller/CommenrController.java @@ -2,6 +2,7 @@ import com.yogit.server.board.dto.request.comment.CreateCommentReq; import com.yogit.server.board.dto.request.comment.DeleteCommentReq; +import com.yogit.server.board.dto.request.comment.PatchCommentReq; import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.board.dto.response.comment.DeleteCommentRes; import com.yogit.server.board.service.comment.CommentService; @@ -81,4 +82,26 @@ public ApplicationResponse> findAllComments(@PathVariable("clip public ApplicationResponse deleteComment(@PathVariable("commentId") Long commentId, @RequestBody @Validated DeleteCommentReq deleteCommentReq){ return commentService.deleteComment(deleteCommentReq, commentId); } + + + /** + * 코멘트 수정 + * @author 토마스 + */ + @ApiOperation(value = "코멘트 수정", notes = "코멘트 아이디, 수정할 내용을 입력해 코멘트 수정 요청.") + @ApiResponses({ + @ApiResponse(code= 201, message = "요청에 성공하였습니다."), + @ApiResponse(code= 404, message = "존재하지 않는 유저입니다."), + @ApiResponse(code= 404, message = "존재하지 않는 클립보드입니다."), + @ApiResponse(code= 404, message = "존재하지 않는 코멘트입니다."), + @ApiResponse(code = 4000 , message = "서버 오류입니다.") + }) + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", required = true, dataTypeClass = Long.class, example = "1"), + @ApiImplicitParam(name = "commentId", required = true, dataTypeClass = Long.class, example = "1") + }) + @PatchMapping("/{commentId}/content") + public ApplicationResponse updateComment(@PathVariable("commentId") Long commentId, @RequestBody @Validated PatchCommentReq patchCommentReq){ + return commentService.updateComment(patchCommentReq, commentId); + } } diff --git a/server/src/main/java/com/yogit/server/board/dto/request/comment/PatchCommentReq.java b/server/src/main/java/com/yogit/server/board/dto/request/comment/PatchCommentReq.java new file mode 100644 index 0000000..b6ad979 --- /dev/null +++ b/server/src/main/java/com/yogit/server/board/dto/request/comment/PatchCommentReq.java @@ -0,0 +1,26 @@ +package com.yogit.server.board.dto.request.comment; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiParam; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Data +@NoArgsConstructor +public class PatchCommentReq { + + @ApiModelProperty(example = "1") + @ApiParam(value = "유저 ID", required = true) + private Long userId; + + @ApiModelProperty(example = "1") + @ApiParam(value = "코멘트 ID", required = true) + private Long commentId; + + @ApiModelProperty(example = "경복궁역 몇 번 출구인가요?") + @ApiParam(value = "클립보드 상세 내용", required = true) + @NotBlank + private String content; +} diff --git a/server/src/main/java/com/yogit/server/board/entity/Comment.java b/server/src/main/java/com/yogit/server/board/entity/Comment.java index 9e315c1..1a59e51 100644 --- a/server/src/main/java/com/yogit/server/board/entity/Comment.java +++ b/server/src/main/java/com/yogit/server/board/entity/Comment.java @@ -1,6 +1,7 @@ package com.yogit.server.board.entity; import com.yogit.server.board.dto.request.comment.CreateCommentReq; +import com.yogit.server.board.dto.request.comment.PatchCommentReq; import com.yogit.server.config.domain.BaseEntity; import com.yogit.server.config.domain.BaseStatus; import com.yogit.server.user.entity.User; @@ -42,4 +43,8 @@ public Comment(CreateCommentReq dto, User user, ClipBoard clipBoard) { public void deleteComment(){ this.setStatus(BaseStatus.INACTIVE); } + + public void updateComment(PatchCommentReq dto){ + this.content = dto.getContent(); + } } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java index fb9ba83..68e3e2f 100644 --- a/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentService.java @@ -2,6 +2,7 @@ import com.yogit.server.board.dto.request.comment.CreateCommentReq; import com.yogit.server.board.dto.request.comment.DeleteCommentReq; +import com.yogit.server.board.dto.request.comment.PatchCommentReq; import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.board.dto.response.comment.DeleteCommentRes; import com.yogit.server.global.dto.ApplicationResponse; @@ -15,4 +16,6 @@ public interface CommentService { ApplicationResponse> findAllComments(Long clipBoardId, Long userId); ApplicationResponse deleteComment(DeleteCommentReq deleteCommentReq, Long commentId); + + ApplicationResponse updateComment(PatchCommentReq patchCommentReq, Long commentId); } diff --git a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java index 0520287..e3932db 100644 --- a/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java +++ b/server/src/main/java/com/yogit/server/board/service/comment/CommentServiceImpl.java @@ -2,6 +2,7 @@ import com.yogit.server.board.dto.request.comment.CreateCommentReq; import com.yogit.server.board.dto.request.comment.DeleteCommentReq; +import com.yogit.server.board.dto.request.comment.PatchCommentReq; import com.yogit.server.board.dto.response.comment.DeleteCommentRes; import com.yogit.server.board.dto.response.comment.CommentRes; import com.yogit.server.board.entity.ClipBoard; @@ -86,4 +87,25 @@ public ApplicationResponse deleteComment(DeleteCommentReq dto, DeleteCommentRes deleteCommentRes = DeleteCommentRes.toDto(comment); return ApplicationResponse.ok(deleteCommentRes); } + + + @Transactional(readOnly = false) + @Override + public ApplicationResponse updateComment(PatchCommentReq dto, Long commentId){ + + User user = userRepository.findById(dto.getUserId()) + .orElseThrow(() -> new NotFoundUserException()); + + Comment comment = commentRepository.findCommentById(dto.getCommentId()) + .orElseThrow(() -> new NotFoundCommentException()); + + //검증: 요청 유저가 코멘트를 생성한 사람인지 + if(!user.getId().equals(comment.getUser().getId())){ + throw new NotHostOfCommentException(); + } + + comment.updateComment(dto); + CommentRes commentRes = CommentRes.toDto(comment); + return ApplicationResponse.ok(commentRes); + } } From 4df593f122d167aa9fe0fe1284dd8d008b3935c0 Mon Sep 17 00:00:00 2001 From: shinhn Date: Thu, 3 Nov 2022 20:49:15 +0900 Subject: [PATCH 11/11] =?UTF-8?q?#5=20feat:=20apple=20login=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 5 + .idea/misc.xml | 2 +- server/build.gradle | 21 +- .../controller/AppleController.java | 147 ++++++++ .../server/applelogin/model/AppsResponse.java | 18 + .../yogit/server/applelogin/model/Key.java | 62 ++++ .../yogit/server/applelogin/model/Keys.java | 19 + .../server/applelogin/model/Payload.java | 144 ++++++++ .../applelogin/model/ServicesResponse.java | 44 +++ .../applelogin/model/TokenResponse.java | 56 +++ .../applelogin/service/AppleService.java | 18 + .../applelogin/service/AppleServiceImpl.java | 77 ++++ .../server/applelogin/util/AppleUtils.java | 330 ++++++++++++++++++ .../applelogin/util/ECPrivateKeyImpl2.java | 185 ++++++++++ .../applelogin/util/HttpClientUtils.java | 120 +++++++ .../src/main/resources/application.properties | 15 + .../resources/static/AuthKey_QLHFNT37VK.p8 | 6 + .../src/main/resources/templates/index.html | 22 ++ 18 files changed, 1289 insertions(+), 2 deletions(-) create mode 100644 server/src/main/java/com/yogit/server/applelogin/controller/AppleController.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/model/AppsResponse.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/model/Key.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/model/Keys.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/model/Payload.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/model/ServicesResponse.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/model/TokenResponse.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/service/AppleService.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/service/AppleServiceImpl.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/util/AppleUtils.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java create mode 100644 server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java create mode 100644 server/src/main/resources/application.properties create mode 100644 server/src/main/resources/static/AuthKey_QLHFNT37VK.p8 create mode 100644 server/src/main/resources/templates/index.html diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 8b8acde..bbc7654 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -12,4 +12,9 @@ + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 7d21bf7..8806c7b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/server/build.gradle b/server/build.gradle index 915df2e..35e5a49 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -6,7 +6,8 @@ plugins { group = 'com.yogit' version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' +sourceCompatibility = '17' +targetCompatibility = '17' configurations { compileOnly { @@ -37,8 +38,26 @@ dependencies { // JWT implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1' + + // apple login + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' // 테스트 용 + implementation 'org.apache.httpcomponents:httpclient' + implementation 'com.nimbusds:nimbus-jose-jwt:3.10' + implementation "io.jsonwebtoken:jjwt:0.9.1" + implementation "org.bouncycastle:bcpkix-jdk15on:1.50" + implementation "org.apache.httpcomponents:httpclient:4.5.13" + implementation fileTree(dir: 'libs', include: '*.jar') } tasks.named('test') { useJUnitPlatform() } + +// apple login +tasks.withType(JavaCompile){ + options.compilerArgs.addAll([ + "--add-exports=java.base/sun.security.pkcs=ALL-UNNAMED", + "--add-exports=java.base/sun.security.util=ALL-UNNAMED", + "--add-exports=java.base/sun.security.x509=ALL-UNNAMED" + ]) +} diff --git a/server/src/main/java/com/yogit/server/applelogin/controller/AppleController.java b/server/src/main/java/com/yogit/server/applelogin/controller/AppleController.java new file mode 100644 index 0000000..75940a2 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/controller/AppleController.java @@ -0,0 +1,147 @@ +package com.yogit.server.applelogin.controller; + +import com.yogit.server.applelogin.model.AppsResponse; +import com.yogit.server.applelogin.model.ServicesResponse; +import com.yogit.server.applelogin.model.TokenResponse; +import com.yogit.server.applelogin.service.AppleService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; + +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +@Controller +public class AppleController { + + private Logger logger = LoggerFactory.getLogger(AppleController.class); + + @Autowired + AppleService appleService; + + /** + * Sign in with Apple - JS Page (index.html) + * + * @param model + * @return + */ + @GetMapping(value = "/") + public String appleLoginPage(ModelMap model) { + + Map metaInfo = appleService.getLoginMetaInfo(); + + model.addAttribute("client_id", metaInfo.get("CLIENT_ID")); + model.addAttribute("redirect_uri", metaInfo.get("REDIRECT_URI")); + model.addAttribute("nonce", metaInfo.get("NONCE")); + + System.out.println(model.getAttribute("client_id")); + System.out.println(model.getAttribute("redirect_uri")); + System.out.println(model.getAttribute("nonce")); + + + return "index"; + } + + /** + * Apple login page Controller (SSL - https) + * + * @param model + * @return + */ + @GetMapping(value = "/apple/login") + public String appleLogin(ModelMap model) { + + Map metaInfo = appleService.getLoginMetaInfo(); + + model.addAttribute("client_id", metaInfo.get("CLIENT_ID")); + model.addAttribute("redirect_uri", metaInfo.get("REDIRECT_URI")); + model.addAttribute("nonce", metaInfo.get("NONCE")); + model.addAttribute("response_type", "code id_token"); + model.addAttribute("scope", "name email"); + model.addAttribute("response_mode", "form_post"); + + System.out.println("=========================="); + System.out.println(model.getAttribute("client_id")); + System.out.println(model.getAttribute("redirect_uri")); + System.out.println(model.getAttribute("nonce")); + System.out.println(model.getAttribute("response_type")); + System.out.println(model.getAttribute("scope")); + System.out.println(model.getAttribute("response_mode")); + + + return "redirect:https://appleid.apple.com/auth/authorize"; + } + + /** + * Apple Login 유저 정보를 받은 후 권한 생성 + * + * @param serviceResponse + * @return + */ + @PostMapping(value = "/redirect") + @ResponseBody + public TokenResponse servicesRedirect(ServicesResponse serviceResponse) throws NoSuchAlgorithmException { + + System.out.println("1-------------"); + if (serviceResponse == null) { + return null; + } + System.out.println("2-------------"); + + + System.out.println(serviceResponse); + System.out.println("3-------------"); + + + String code = serviceResponse.getCode(); + System.out.println(code); + System.out.println("4-------------"); + + String id_token = serviceResponse.getId_token(); + System.out.println(id_token); + System.out.println("5-------------"); + + String client_secret = appleService.getAppleClientSecret(serviceResponse.getId_token()); + System.out.println(client_secret); + System.out.println("6-------------"); + + + logger.debug("================================"); + logger.debug("id_token ‣ " + serviceResponse.getId_token()); + logger.debug("payload ‣ " + appleService.getPayload(serviceResponse.getId_token())); + logger.debug("client_secret ‣ " + client_secret); + logger.debug("================================"); + + System.out.println("7-------------"); + + return appleService.requestCodeValidations(client_secret, code, null); + } + + /** + * refresh_token 유효성 검사 + * + * @param client_secret + * @param refresh_token + * @return + */ + @PostMapping(value = "/refresh") + @ResponseBody + public TokenResponse refreshRedirect(@RequestParam String client_secret, @RequestParam String refresh_token) { + return appleService.requestCodeValidations(client_secret, null, refresh_token); + } + + /** + * Apple 유저의 이메일 변경, 서비스 해지, 계정 탈퇴에 대한 Notifications을 받는 Controller (SSL - https (default: 443)) + * + * @param appsResponse + */ + @PostMapping(value = "/apps/to/endpoint") + @ResponseBody + public void appsToEndpoint(@RequestBody AppsResponse appsResponse) { + logger.debug("[/path/to/endpoint] RequestBody ‣ " + appsResponse.getPayload()); + } + +} diff --git a/server/src/main/java/com/yogit/server/applelogin/model/AppsResponse.java b/server/src/main/java/com/yogit/server/applelogin/model/AppsResponse.java new file mode 100644 index 0000000..13f6d21 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/model/AppsResponse.java @@ -0,0 +1,18 @@ +package com.yogit.server.applelogin.model; + + +public class AppsResponse { + + private String payload; + + public String getPayload() { + return payload; + } + + public void setPayload(String payload) { + this.payload = payload; + } + + public AppsResponse() { + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/model/Key.java b/server/src/main/java/com/yogit/server/applelogin/model/Key.java new file mode 100644 index 0000000..81eecf6 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/model/Key.java @@ -0,0 +1,62 @@ +package com.yogit.server.applelogin.model; + +public class Key { + + private String kty; + private String kid; + private String use; + private String alg; + private String n; + private String e; + + public String getKty() { + return kty; + } + + public void setKty(String kty) { + this.kty = kty; + } + + public String getKid() { + return kid; + } + + public void setKid(String kid) { + this.kid = kid; + } + + public String getUse() { + return use; + } + + public void setUse(String use) { + this.use = use; + } + + public String getAlg() { + return alg; + } + + public void setAlg(String alg) { + this.alg = alg; + } + + public String getN() { + return n; + } + + public void setN(String n) { + this.n = n; + } + + public String getE() { + return e; + } + + public void setE(String e) { + this.e = e; + } + + public Key() { + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/model/Keys.java b/server/src/main/java/com/yogit/server/applelogin/model/Keys.java new file mode 100644 index 0000000..37b0988 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/model/Keys.java @@ -0,0 +1,19 @@ +package com.yogit.server.applelogin.model; + +import java.util.List; + +public class Keys { + + private List keys; + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } + + public Keys() { + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/model/Payload.java b/server/src/main/java/com/yogit/server/applelogin/model/Payload.java new file mode 100644 index 0000000..6ef6f1a --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/model/Payload.java @@ -0,0 +1,144 @@ +package com.yogit.server.applelogin.model; + +public class Payload { + + private String iss; + private String aud; + private Long exp; + private Long iat; + private String sub; + private String nonce; + private String c_hash; + private String at_hash; + private String email; + private String email_verified; + private String is_private_email; + private Long auth_time; + private boolean nonce_supported; + + public String getIss() { + return iss; + } + + public void setIss(String iss) { + this.iss = iss; + } + + public String getAud() { + return aud; + } + + public void setAud(String aud) { + this.aud = aud; + } + + public Long getExp() { + return exp; + } + + public void setExp(Long exp) { + this.exp = exp; + } + + public Long getIat() { + return iat; + } + + public void setIat(Long iat) { + this.iat = iat; + } + + public String getSub() { + return sub; + } + + public void setSub(String sub) { + this.sub = sub; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } + + public String getC_hash() { + return c_hash; + } + + public void setC_hash(String c_hash) { + this.c_hash = c_hash; + } + + public String getAt_hash() { + return at_hash; + } + + public void setAt_hash(String at_hash) { + this.at_hash = at_hash; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getEmail_verified() { + return email_verified; + } + + public void setEmail_verified(String email_verified) { + this.email_verified = email_verified; + } + + public String getIs_private_email() { + return is_private_email; + } + + public void setIs_private_email(String is_private_email) { + this.is_private_email = is_private_email; + } + + public Long getAuth_time() { + return auth_time; + } + + public void setAuth_time(Long auth_time) { + this.auth_time = auth_time; + } + + public boolean isNonce_supported() { + return nonce_supported; + } + + public void setNonce_supported(boolean nonce_supported) { + this.nonce_supported = nonce_supported; + } + + @Override + public String toString() { + return "{" + + "iss='" + iss + '\'' + + ", aud='" + aud + '\'' + + ", exp=" + exp + + ", iat=" + iat + + ", sub='" + sub + '\'' + + ", nonce='" + nonce + '\'' + + ", c_hash='" + c_hash + '\'' + + ", at_hash='" + at_hash + '\'' + + ", email='" + email + '\'' + + ", email_verified='" + email_verified + '\'' + + ", is_private_email='" + is_private_email + '\'' + + ", auth_time=" + auth_time + + ", nonce_supported=" + nonce_supported + + '}'; + } + + public Payload() { + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/model/ServicesResponse.java b/server/src/main/java/com/yogit/server/applelogin/model/ServicesResponse.java new file mode 100644 index 0000000..fb4ca02 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/model/ServicesResponse.java @@ -0,0 +1,44 @@ +package com.yogit.server.applelogin.model; + +public class ServicesResponse { + + private String state; + private String code; + private String id_token; + private String user; + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getId_token() { + return id_token; + } + + public void setId_token(String id_token) { + this.id_token = id_token; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public ServicesResponse() { + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/model/TokenResponse.java b/server/src/main/java/com/yogit/server/applelogin/model/TokenResponse.java new file mode 100644 index 0000000..28738e4 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/model/TokenResponse.java @@ -0,0 +1,56 @@ +package com.yogit.server.applelogin.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown=true) +public class TokenResponse { + + private String access_token; + private Long expires_in; + private String id_token; + private String refresh_token; + private String token_type; + + public String getAccess_token() { + return access_token; + } + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public Long getExpires_in() { + return expires_in; + } + + public void setExpires_in(Long expires_in) { + this.expires_in = expires_in; + } + + public String getId_token() { + return id_token; + } + + public void setId_token(String id_token) { + this.id_token = id_token; + } + + public String getRefresh_token() { + return refresh_token; + } + + public void setRefresh_token(String refresh_token) { + this.refresh_token = refresh_token; + } + + public String getToken_type() { + return token_type; + } + + public void setToken_type(String token_type) { + this.token_type = token_type; + } + + public TokenResponse() { + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/service/AppleService.java b/server/src/main/java/com/yogit/server/applelogin/service/AppleService.java new file mode 100644 index 0000000..078847f --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/service/AppleService.java @@ -0,0 +1,18 @@ +package com.yogit.server.applelogin.service; + +import com.yogit.server.applelogin.model.TokenResponse; + +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +public interface AppleService { + + String getAppleClientSecret(String id_token) throws NoSuchAlgorithmException; + + TokenResponse requestCodeValidations(String client_secret, String code, String refresh_token); + + Map getLoginMetaInfo(); + + String getPayload(String id_token); + +} diff --git a/server/src/main/java/com/yogit/server/applelogin/service/AppleServiceImpl.java b/server/src/main/java/com/yogit/server/applelogin/service/AppleServiceImpl.java new file mode 100644 index 0000000..6cdd73f --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/service/AppleServiceImpl.java @@ -0,0 +1,77 @@ +package com.yogit.server.applelogin.service; + +import com.yogit.server.applelogin.model.TokenResponse; +import com.yogit.server.applelogin.util.AppleUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +@Service +public class AppleServiceImpl implements AppleService { + + @Autowired + AppleUtils appleUtils; + + /** + * 유효한 id_token인 경우 client_secret 생성 + * + * @param id_token + * @return + */ + @Override + public String getAppleClientSecret(String id_token) throws NoSuchAlgorithmException { + + if (appleUtils.verifyIdentityToken(id_token)) { + return appleUtils.createClientSecret(); + } + + return null; + } + + /** + * code 또는 refresh_token가 유효한지 Apple Server에 검증 요청 + * + * @param client_secret + * @param code + * @param refresh_token + * @return + */ + @Override + public TokenResponse requestCodeValidations(String client_secret, String code, String refresh_token) { + + TokenResponse tokenResponse = new TokenResponse(); + + // 만약 처음 인증하는 유저여서 refresh토큰 없으면 client_secret, authorization_code로 검증 + if (client_secret != null && code != null && refresh_token == null) { + tokenResponse = appleUtils.validateAuthorizationGrantCode(client_secret, code); + } + // 이미 refresh토큰잇는 유저면 client_secret, refresh_token로 검증 + else if (client_secret != null && code == null && refresh_token != null) { + tokenResponse = appleUtils.validateAnExistingRefreshToken(client_secret, refresh_token); + } + + return tokenResponse; + } + + /** + * Apple login page 호출을 위한 Meta 정보 가져오기 + * + * @return + */ + @Override + public Map getLoginMetaInfo() { + return appleUtils.getMetaInfo(); + } + + /** + * id_token에서 payload 데이터 가져오기 + * + * @return + */ + @Override + public String getPayload(String id_token) { + return appleUtils.decodeFromIdToken(id_token).toString(); + } +} diff --git a/server/src/main/java/com/yogit/server/applelogin/util/AppleUtils.java b/server/src/main/java/com/yogit/server/applelogin/util/AppleUtils.java new file mode 100644 index 0000000..6044a42 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/util/AppleUtils.java @@ -0,0 +1,330 @@ +package com.yogit.server.applelogin.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.*; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.ReadOnlyJWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.yogit.server.applelogin.model.Key; +import com.yogit.server.applelogin.model.Keys; +import com.yogit.server.applelogin.model.TokenResponse; +import net.minidev.json.JSONObject; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.text.ParseException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Component +public class AppleUtils { + + @Value("${APPLE.PUBLICKEY.URL}") + private String APPLE_PUBLIC_KEYS_URL; + + @Value("${APPLE.ISS}") + private String ISS; + + @Value("${APPLE.AUD}") + private String AUD; + + @Value("${APPLE.TEAM.ID}") + private String TEAM_ID; + + @Value("${APPLE.KEY.ID}") + private String KEY_ID; + + @Value("${APPLE.KEY.PATH}") + private String KEY_PATH; + + @Value("${APPLE.AUTH.TOKEN.URL}") + private String AUTH_TOKEN_URL; + + @Value("${APPLE.WEBSITE.URL}") + private String APPLE_WEBSITE_URL; + + /** + * User가 Sign in with Apple 요청(https://appleid.apple.com/auth/authorize)으로 전달받은 id_token을 이용한 최초 검증 + * Apple Document URL ‣ https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/verifying_a_user + * + * @param id_token + * @return boolean + */ + public boolean verifyIdentityToken(String id_token) { + + try { + SignedJWT signedJWT = SignedJWT.parse(id_token); + ReadOnlyJWTClaimsSet payload = signedJWT.getJWTClaimsSet(); + + // EXP 만료시간 검증 + Date currentTime = new Date(System.currentTimeMillis()); + if (!currentTime.before(payload.getExpirationTime())) { + return false; + } + + // NONCE(Test value), ISS, AUD + if (!"20B20D-0S8-1K8".equals(payload.getClaim("nonce")) || !ISS.equals(payload.getIssuer()) || !AUD.equals(payload.getAudience().get(0))) { + return false; + } + + // RSA + if (verifyPublicKey(signedJWT)) { + return true; + } + } catch (ParseException e) { + e.printStackTrace(); + } + + return false; + } + + /** + * Apple Server에서 공개 키를 받아서 서명 확인 + * + * @param signedJWT + * @return + */ + private boolean verifyPublicKey(SignedJWT signedJWT) { + + try { + String publicKeys = HttpClientUtils.doGet(APPLE_PUBLIC_KEYS_URL); + ObjectMapper objectMapper = new ObjectMapper(); + Keys keys = objectMapper.readValue(publicKeys, Keys.class); + for (Key key : keys.getKeys()) { + RSAKey rsaKey = (RSAKey) JWK.parse(objectMapper.writeValueAsString(key)); + RSAPublicKey publicKey = rsaKey.toRSAPublicKey(); + JWSVerifier verifier = new RSASSAVerifier(publicKey); + + if (signedJWT.verify(verifier)) { + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return false; + } + + /** + * client_secret 생성 + * Apple Document URL ‣ https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens + * + * @return client_secret(jwt) + */ + public String createClientSecret() { + + JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256).keyID(KEY_ID).build(); + JWTClaimsSet claimsSet = new JWTClaimsSet(); + Date now = new Date(); + + claimsSet.setIssuer(TEAM_ID); + claimsSet.setIssueTime(now); + claimsSet.setExpirationTime(new Date(now.getTime() + 3600000)); + claimsSet.setAudience(ISS); + claimsSet.setSubject(AUD); + + SignedJWT jwt = new SignedJWT(header, claimsSet); + + try { +// ECPrivateKey ecPrivateKey = new ECPrivateKeyImpl2(readPrivateKey()); +// JWSSigner jwsSigner = new ECDSASigner(ecPrivateKey.getS()); +// +// jwt.sign(jwsSigner); + System.out.println("=====새로운 시도 1====="); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(readPrivateKey()); + System.out.println("=====새로운 시도 5====="); + + + try{ + KeyFactory kf = KeyFactory.getInstance("EC"); + System.out.println("=====새로운 시도 6====="); + ECPrivateKey ecPrivateKey = (ECPrivateKey) kf.generatePrivate(spec); + System.out.println("=====새로운 시도 7====="); + System.out.println("=====새로운 시도 7.5====="+ecPrivateKey.getS()); + JWSSigner jwsSigner = new ECDSASigner(ecPrivateKey.getS()); + System.out.println("=====새로운 시도 8====="); + jwt.sign(jwsSigner); + System.out.println("=====새로운 시도 9====="); + }catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + }catch (InvalidKeySpecException e){ + e.printStackTrace(); + } + + } +// catch (InvalidKeyException e) { +// e.printStackTrace(); +// } + catch (JOSEException e) { + e.printStackTrace(); + } + + return jwt.serialize(); + } + + /** + * 파일에서 private key 획득 + * + * @return Private Key + */ + private byte[] readPrivateKey() { + + ClassPathResource resource = new ClassPathResource(KEY_PATH); + + +// Resource resource = new ClassPathResource(KEY_PATH); + byte[] content = null; + System.out.println("=====새로운 시도 2====="); + try (Reader keyReader = new InputStreamReader(resource.getInputStream()); + + PemReader pemReader = new PemReader(keyReader)) { + { + System.out.println("=====새로운 시도 3====="); + PemObject pemObject = pemReader.readPemObject(); + System.out.println("=====새로운 시도 3.5====="); + content = pemObject.getContent(); + System.out.println("=====새로운 시도 4====="); + System.out.println(content+"=====content====="); + } + } catch (IOException e) { + e.printStackTrace(); + } + + return content; + } + + /** + * 유효한 code 인지 Apple Server에 확인 요청 + * Apple Document URL ‣ https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens + * + * @return + */ + public TokenResponse validateAuthorizationGrantCode(String client_secret, String code) { + + Map tokenRequest = new HashMap<>(); + + tokenRequest.put("client_id", AUD); + tokenRequest.put("client_secret", client_secret); + tokenRequest.put("code", code); + tokenRequest.put("grant_type", "authorization_code"); + tokenRequest.put("redirect_uri", APPLE_WEBSITE_URL); + + return getTokenResponse(tokenRequest); + } + + /** + * 유효한 refresh_token 인지 Apple Server에 확인 요청 + * Apple Document URL ‣ https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens + * + * @param client_secret + * @param refresh_token + * @return + */ + public TokenResponse validateAnExistingRefreshToken(String client_secret, String refresh_token) { + + Map tokenRequest = new HashMap<>(); + + tokenRequest.put("client_id", AUD); + tokenRequest.put("client_secret", client_secret); + tokenRequest.put("grant_type", "refresh_token"); + tokenRequest.put("refresh_token", refresh_token); + + return getTokenResponse(tokenRequest); + } + + /** + * POST https://appleid.apple.com/auth/token + * + * @param tokenRequest + * @return + */ + private TokenResponse getTokenResponse(Map tokenRequest) { + + try { + System.out.println("======tokenRequest"+tokenRequest); + String response = HttpClientUtils.doPost(AUTH_TOKEN_URL, tokenRequest); + System.out.println("======response"+response); + + ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); // null허용 설정 추가 + System.out.println("======objectMapper"+objectMapper); + + TokenResponse tokenResponse = objectMapper.readValue(response, TokenResponse.class); + System.out.println("======tokenResponse"+tokenResponse); + + if (tokenRequest != null) { + return tokenResponse; + } + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * Apple Meta Value + * + * @return + */ + public Map getMetaInfo() { + + Map metaInfo = new HashMap<>(); + + metaInfo.put("CLIENT_ID", AUD); + metaInfo.put("REDIRECT_URI", APPLE_WEBSITE_URL); + metaInfo.put("NONCE", "20B20D-0S8-1K8"); // Test value + + return metaInfo; + } + + /** + * id_token을 decode해서 payload 값 가져오기 + * + * @param id_token + * @return + */ + public JSONObject decodeFromIdToken(String id_token) { + + try { + SignedJWT signedJWT = SignedJWT.parse(id_token); + System.out.println("=====payload1"+signedJWT ); + ReadOnlyJWTClaimsSet getPayload = signedJWT.getJWTClaimsSet(); + System.out.println("=====payload2"+getPayload); + ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false); +// objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT); // null 값 허용 +// objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT); +// Payload payload = objectMapper.readValue(getPayload.toJSONObject().toJSONString(), Payload.class); + JSONObject payload = objectMapper.readValue(getPayload.toJSONObject().toJSONString(), JSONObject.class); + + System.out.println("=====payload3"+payload ); + if (payload != null) { + return payload; + } + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + +} diff --git a/server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java b/server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java new file mode 100644 index 0000000..ecb82e8 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/util/ECPrivateKeyImpl2.java @@ -0,0 +1,185 @@ +package com.yogit.server.applelogin.util; + +import sun.security.pkcs.PKCS8Key; +import sun.security.util.*; +import sun.security.x509.AlgorithmId; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; + +/** + * Key implementation for EC private keys. + * + * ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft): + * + *
+ * EXPLICIT TAGS
+ *
+ * ECPrivateKey ::= SEQUENCE {
+ *   version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ *   privateKey OCTET STRING,
+ *   parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
+ *   publicKey [1] BIT STRING OPTIONAL
+ * }
+ * 
+ * + * We currently ignore the optional parameters and publicKey fields. We + * require that the parameters are encoded as part of the AlgorithmIdentifier, + * not in the private key structure. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class ECPrivateKeyImpl2 extends PKCS8Key implements ECPrivateKey { + + private static final long serialVersionUID = 88695385615075129L; + + private BigInteger s; // private value + private byte[] arrayS; // private value as a little-endian array + private ECParameterSpec params; + + /** + * Construct a key from its encoding. Called by the ECKeyFactory. + */ + ECPrivateKeyImpl2(byte[] encoded) throws InvalidKeyException { + super(encoded); + parseKeyBits(); + } + + /** + * Construct a key from its components. Used by the + * KeyFactory. + */ + ECPrivateKeyImpl2(BigInteger s, ECParameterSpec params) + throws InvalidKeyException { + this.s = s; + this.params = params; + makeEncoding(s); + + } + + ECPrivateKeyImpl2(byte[] s, ECParameterSpec params) + throws InvalidKeyException { + this.arrayS = s.clone(); + this.params = params; + makeEncoding(s); + } + + private void makeEncoding(byte[] s) throws InvalidKeyException { + algid = new AlgorithmId + (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params)); + try { + DerOutputStream out = new DerOutputStream(); + out.putInteger(1); // version 1 + byte[] privBytes = s.clone(); + ArrayUtil.reverse(privBytes); + out.putOctetString(privBytes); + Arrays.fill(privBytes, (byte)0); + DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); + key = val.toByteArray(); + val.clear(); + } catch (IOException exc) { + // should never occur + throw new InvalidKeyException(exc); + } + } + + private void makeEncoding(BigInteger s) throws InvalidKeyException { + algid = new AlgorithmId(AlgorithmId.EC_oid, + ECParameters.getAlgorithmParameters(params)); + try { + byte[] sArr = s.toByteArray(); + // convert to fixed-length array + int numOctets = (params.getOrder().bitLength() + 7) / 8; + byte[] sOctets = new byte[numOctets]; + int inPos = Math.max(sArr.length - sOctets.length, 0); + int outPos = Math.max(sOctets.length - sArr.length, 0); + int length = Math.min(sArr.length, sOctets.length); + System.arraycopy(sArr, inPos, sOctets, outPos, length); + Arrays.fill(sArr, (byte)0); + + DerOutputStream out = new DerOutputStream(); + out.putInteger(1); // version 1 + out.putOctetString(sOctets); + Arrays.fill(sOctets, (byte)0); + DerValue val = DerValue.wrap(DerValue.tag_Sequence, out); + key = val.toByteArray(); + val.clear(); + } catch (IOException exc) { + throw new AssertionError("Should not happen", exc); + } + } + + // see JCA doc + public String getAlgorithm() { + return "EC"; + } + + // see JCA doc + public BigInteger getS() { + if (s == null) { + byte[] arrCopy = arrayS.clone(); + ArrayUtil.reverse(arrCopy); + s = new BigInteger(1, arrCopy); + Arrays.fill(arrCopy, (byte)0); + } + return s; + } + + public byte[] getArrayS() { + if (arrayS == null) { + arrayS = ECUtil.sArray(getS(), params); + } + return arrayS.clone(); + } + + // see JCA doc + public ECParameterSpec getParams() { + return params; + } + + private void parseKeyBits() throws InvalidKeyException { + try { + DerInputStream in = new DerInputStream(key); + DerValue derValue = in.getDerValue(); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + DerInputStream data = derValue.data; + int version = data.getInteger(); + if (version != 1) { + throw new IOException("Version must be 1"); + } + byte[] privData = data.getOctetString(); + ArrayUtil.reverse(privData); + arrayS = privData; + while (data.available() != 0) { + DerValue value = data.getDerValue(); + if (value.isContextSpecific((byte) 0)) { + // ignore for now + } else if (value.isContextSpecific((byte) 1)) { + // ignore for now + } else { + throw new InvalidKeyException("Unexpected value: " + value); + } + } + AlgorithmParameters algParams = this.algid.getParameters(); + if (algParams == null) { + throw new InvalidKeyException("EC domain parameters must be " + + "encoded in the algorithm identifier"); + } + params = algParams.getParameterSpec(ECParameterSpec.class); + } catch (IOException e) { + throw new InvalidKeyException("Invalid EC private key", e); + } catch (InvalidParameterSpecException e) { + throw new InvalidKeyException("Invalid EC private key", e); + } + } +} + diff --git a/server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java b/server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java new file mode 100644 index 0000000..63efdf0 --- /dev/null +++ b/server/src/main/java/com/yogit/server/applelogin/util/HttpClientUtils.java @@ -0,0 +1,120 @@ +package com.yogit.server.applelogin.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class HttpClientUtils { + + private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class); + private static ObjectMapper objectMapper = new ObjectMapper(); + + public static String doGet(String url) { + String result = null; + CloseableHttpClient httpclient = null; + CloseableHttpResponse response = null; + Integer statusCode = null; + String reasonPhrase = null; + + try { + httpclient = HttpClients.createDefault(); + HttpGet get = new HttpGet(url); + response = httpclient.execute(get); + statusCode = response.getStatusLine().getStatusCode(); + reasonPhrase = response.getStatusLine().getReasonPhrase(); + HttpEntity entity = response.getEntity(); + result = EntityUtils.toString(entity, "UTF-8"); + EntityUtils.consume(entity); + + if (statusCode != 200) { + logger.error(String.format("[doGet]http get url(%s) failed. status code:%s. reason:%s. result:%s", url, statusCode, reasonPhrase, result)); + } + } catch (Throwable t) { + logger.error(String.format("[doGet]http get url(%s) failed. status code:%s. reason:%s.", url, statusCode, reasonPhrase), t); + } finally { + try { + if (response != null) { + response.close(); + } + if (httpclient != null) { + httpclient.close(); + } + } catch (IOException e) { + logger.error(String.format("[doGet]release http get resource failed. url(%s). reason:%s.", url, e.getMessage())); + } + } + + return result; + } + + public static String doPost(String url, Map param) { + String result = null; + CloseableHttpClient httpclient = null; + CloseableHttpResponse response = null; + Integer statusCode = null; + String reasonPhrase = null; + try { + httpclient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(url); + httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded"); + List nvps = new ArrayList<>(); + Set> entrySet = param.entrySet(); + for (Entry entry : entrySet) { + String fieldName = entry.getKey(); + String fieldValue = entry.getValue(); + nvps.add(new BasicNameValuePair(fieldName, fieldValue)); + } + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nvps); + httpPost.setEntity(formEntity); + response = httpclient.execute(httpPost); + statusCode = response.getStatusLine().getStatusCode(); + reasonPhrase = response.getStatusLine().getReasonPhrase(); + HttpEntity entity = response.getEntity(); + result = EntityUtils.toString(entity, "UTF-8"); + + if (statusCode != 200) { + logger.error(String.format("[doPost]post url(%s) failed. status code:%s. reason:%s. param:%s. result:%s", url, statusCode, reasonPhrase, objectMapper.writeValueAsString(param), result)); + } + EntityUtils.consume(entity); + } catch (Throwable t) { + try { + logger.error(String.format("[doPost]post url(%s) failed. status code:%s. reason:%s. param:%s.", url, statusCode, reasonPhrase, objectMapper.writeValueAsString(param)), t); + } catch (JsonProcessingException e) { + } + } finally { + try { + if (response != null) { + response.close(); + } + if (httpclient != null) { + httpclient.close(); + } + } catch (IOException e) { + try { + logger.error(String.format("[doPost]release http post resource failed. url(%s). reason:%s, param:%s.", url, e.getMessage(), objectMapper.writeValueAsString(param))); + } catch (JsonProcessingException ex) { + } + } + } + return result; + } + +} diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties new file mode 100644 index 0000000..b0b6d37 --- /dev/null +++ b/server/src/main/resources/application.properties @@ -0,0 +1,15 @@ +logging.level.com.whitepaek.demosigninwithapple=DEBUG + +APPLE.AUTH.TOKEN.URL=https://appleid.apple.com/auth/token +APPLE.PUBLICKEY.URL=https://appleid.apple.com/auth/keys +# redirect url 정보 +APPLE.WEBSITE.URL=https://yogit.world/redirect +APPLE.ISS=https://appleid.apple.com +# client_ID +APPLE.AUD=com.Branch.service +#Team_ID +APPLE.TEAM.ID=9487SKDZZB +# kid +APPLE.KEY.ID=QLHFNT37VK +# key id path : AuthKey_[key_id], 애플 사이트에서 다운 받아야 함 +APPLE.KEY.PATH=static/AuthKey_QLHFNT37VK.p8 \ No newline at end of file diff --git a/server/src/main/resources/static/AuthKey_QLHFNT37VK.p8 b/server/src/main/resources/static/AuthKey_QLHFNT37VK.p8 new file mode 100644 index 0000000..59a3419 --- /dev/null +++ b/server/src/main/resources/static/AuthKey_QLHFNT37VK.p8 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg6bpEOxRZ0VuuNgJ6 +drfzrk9/9v0uNhEaXqpqpiEdT1OgCgYIKoZIzj0DAQehRANCAARB7v5hC6Pv68oB +H/gxjfHwjDrLTQBzXqWhjjVjK8UOf0vhGH2uGQ09KfFdt1JSfFJHycpkF4+VkwsQ +yDh9qWTX +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/server/src/main/resources/templates/index.html b/server/src/main/resources/templates/index.html new file mode 100644 index 0000000..15f03c3 --- /dev/null +++ b/server/src/main/resources/templates/index.html @@ -0,0 +1,22 @@ + + + + + Apple sign-in + + +

웰컴 페이지

+
+ + + + \ No newline at end of file