Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

프로젝트 댓글 저장/수정/삭제/조회(목록) api 컨트롤러 및 스웨거 설정 #82

Merged
merged 10 commits into from
Mar 5, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package sixgaezzang.sidepeek.comments.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import sixgaezzang.sidepeek.comments.dto.request.CommentRequest;
import sixgaezzang.sidepeek.comments.dto.response.CommentResponse;
import sixgaezzang.sidepeek.common.annotation.Login;

@RestController
@RequestMapping("/projects/{projectId}/comments")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 /projects/{projectId}를 제거하는 것은 어떨까요?
예전에 Rest API 관련 글을 읽었을 때, /comments/{comment_id}만 있어도 댓글 식별이 가능하기 때문에 project/.../comment와 같은 계층 구조를 가지고 가지 않아도 된다는 이야기를 들은 적이 있어서용!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오홍 감사합니다! 생각해보니Id만으로도 식별할 수 있으니 필요는 없겠네유!!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 댓글이 Project에 종속되어 있다고 생각하기 때문에, /projects/{projectId}를 하면 사용자가 특정 프로젝트의 모든 관련 댓글을 쉽게 찾을 수 있기 때문에 남겼으면 좋겠다는 생각이 또 드네요!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 url은 그대로 놓고 나중에 이야기 해볼게유우~!!

@Tag(name = "Project Comment", description = "Project Comment API")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 아직 Swagger에 익숙하지 않아서 그런데.. 혹시 @Tag를 쓰신 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 저도 이미 적용한 코드 따로 붙이긴 한건데 이렇게 붙이면 스웨거 페이지에서 Tag 설정을 하면 아래와 같이 그 큰 제목이라고 해야될까유! 그게 글이 저희가 태그로 설정한걸로 바뀝니다!
image

image

@RequiredArgsConstructor
public class CommentController {

@PostMapping
@Operation(summary = "댓글 생성")
@ApiResponse(responseCode = "201", description = "댓글 생성 성공")
public ResponseEntity<CommentResponse> save(
@Schema(description = "로그인한 회원 식별자", example = "1")
@Login
Long loginId,

@Schema(description = "생성할 댓글의 프로젝트 식별자", example = "1")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

만약 url에서 /projects/{projectId}을 제거하게 된다면 요부분은 바디로 받아도 될 것 같습니당!

@PathVariable
Long projectId,

@Valid
@RequestBody
CommentRequest request
) {
return null;
}

@PutMapping("/{id}")
@Operation(summary = "댓글 수정")
@ApiResponse(responseCode = "200", description = "댓글 수정 성공")
public ResponseEntity<CommentResponse> update(
@Schema(description = "로그인한 회원 식별자", example = "1")
@Login
Long loginId,

@Schema(description = "수정할 댓글의 프로젝트 식별자", example = "1")
@PathVariable
Long projectId,

@Schema(description = "수정할 댓글 식별자", example = "1")
@PathVariable(value = "id")
Long commentId,

@Valid
@RequestBody
CommentRequest request
) {
return null;
}

@DeleteMapping("/{id}")
@Operation(summary = "댓글 삭제")
@ApiResponse(responseCode = "204", description = "댓글 삭제 성공")
public ResponseEntity<Void> delete(
@Schema(description = "로그인한 회원 식별자", example = "1")
@Login
Long loginId,

@Schema(description = "삭제할 댓글의 프로젝트 식별자", example = "1")
@PathVariable
Long projectId,

@Schema(description = "삭제할 댓글 식별자", example = "1")
@PathVariable(value = "id")
Long commentId
) {
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package sixgaezzang.sidepeek.comments.dto.request;

import static sixgaezzang.sidepeek.comments.exception.message.CommentErrorMessage.CONTENT_IS_NULL;
import static sixgaezzang.sidepeek.comments.exception.message.CommentErrorMessage.CONTENT_OVER_MAX_LENGTH;
import static sixgaezzang.sidepeek.comments.exception.message.CommentErrorMessage.IS_ANONYMOUS_IS_NULL;
import static sixgaezzang.sidepeek.comments.util.CommentConstant.MAX_COMMENT_LENGTH;
import static sixgaezzang.sidepeek.common.util.CommonConstant.MIN_ID;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

@Schema(description = "댓글 생성/수정 요청 정보")
public record CommentRequest(
@Schema(description = "댓글 작성자 식별자", example = "1")
@Min(value = MIN_ID, message = "작성자 id는 " + MIN_ID + "보다 작을 수 없습니다.")
Long ownerId,

@Schema(description = "익명 댓글 여부", example = "false")
@NotNull(message = IS_ANONYMOUS_IS_NULL)
Boolean isAnonymous,

@Schema(description = "댓글 내용", example = "우와 이 프로젝트 대박인데요?")
@Size(max = MAX_COMMENT_LENGTH, message = CONTENT_OVER_MAX_LENGTH)
@NotBlank(message = CONTENT_IS_NULL)
String content
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sixgaezzang.sidepeek.comments.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;

@Schema(description = "댓글 목록 응답 정보")
public record CommentListResponse(
@Schema(description = "댓글 목록 정보(오래된 순)")
List<CommentResponse> comments
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package sixgaezzang.sidepeek.comments.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import sixgaezzang.sidepeek.users.dto.response.UserSummary;
// TODO: 대댓글 반영 부탁해용!!

@Schema(description = "댓글 응답 정보")
public record CommentResponse(
@Schema(description = "댓글 식별자", example = "1")
Long id,

@Schema(description = "댓글 작성자 정보(익명 댓글은 null)")
UserSummary owner,

@Schema(description = "댓글 작성자와 로그인 사용자 일치 여부")
boolean isOwner,
Comment on lines +17 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내 댓글 때문인거죠? 이건 생각도 못했네용!! 감사합니다👍🏻👍🏻

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서는 익명이라 작성자 Id를 보낼 수 없어서 서버단에서 처리가 필요했습니다! 알아봐주셔서 감사합니당!🥰


@Schema(description = "익명 댓글 여부")
boolean isAnonymous,

@Schema(description = "댓글 내용", example = "우와 이 프로젝트 대박인데요?")
String content,

@Schema(description = "댓글 생성 시각", example = "2024-03-02 20:17:00", type = "string")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime createdAt
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package sixgaezzang.sidepeek.comments.exception.message;

import static sixgaezzang.sidepeek.comments.util.CommentConstant.MAX_COMMENT_LENGTH;

public class CommentErrorMessage {
// isAnonymous
public static final String IS_ANONYMOUS_IS_NULL = "익명 댓글 여부를 입력해주세요.";

// content
public static final String CONTENT_IS_NULL = "댓글 내용을 입력해주세요.";
public static final String CONTENT_OVER_MAX_LENGTH = "댓글은 " + MAX_COMMENT_LENGTH + "자 이하여야 합니다.";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package sixgaezzang.sidepeek.comments.util;

public class CommentConstant {
Sehee-Lee-01 marked this conversation as resolved.
Show resolved Hide resolved
public static final int MAX_COMMENT_LENGTH = 300;
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ public List<ProjectSkillSummary> saveAll(Project project, List<ProjectSkillSaveR
.map(ProjectSkillSummary::from)
.toList();
}



public List<ProjectSkill> findAll(Project project) {
return projectSkillRepository.findAllByProject(project);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public static void validateDescription(String description) {
validateTextLength(description, DESCRIPTION_OVER_MAX_LENGTH);
}


public static void validateOwnerId(Long ownerId) {
Assert.notNull(ownerId, OWNER_ID_IS_NULL);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package sixgaezzang.sidepeek.projects.service;


import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static sixgaezzang.sidepeek.projects.exception.message.FileErrorMessage.OVERVIEW_IMAGE_OVER_MAX_COUNT;
Expand Down Expand Up @@ -84,7 +83,6 @@ void setup() {
assertThat(savedImageUrls).isNull();
}


@Test
void 목록_개수가_최대를_넘어서_파일_목록_저장에_실패한다() {
// given, when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,16 @@ class ProjectSkillServiceTest {

@Autowired
ProjectSkillService projectSkillService;

@Autowired
ProjectSkillRepository projectSkillRepository;

@Autowired
SkillRepository skillRepository;

@Autowired
ProjectRepository projectRepository;

@Autowired
UserRepository userRepository;

Expand Down
Loading