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

[BE] docs: 답변 하이라이트 API 문서 작성 #800

Merged
merged 8 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/src/docs/asciidoc/highlight-answers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
==== 리뷰 하이라이트 변경 (추가, 삭제, 수정 포함)

operation::highlight-answer[snippets="curl-request,request-cookies,http-response,request-fields"]
6 changes: 5 additions & 1 deletion backend/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ include::review-list.adoc[]

=== 리뷰 모아보기

include::review-gather.adoc[]
include::review-gather.adoc[]

=== 답변 하이라이트

include::highlight-answers.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package reviewme.highlight.controller;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;
import reviewme.highlight.service.HighlightService;
import reviewme.highlight.service.dto.HighlightsRequest;

@RestController
@RequiredArgsConstructor
public class HighlightController {

private final HighlightService highlightService;

@PostMapping("/v2/highlight")
public ResponseEntity<Void> highlight(@Valid @RequestBody HighlightsRequest request,
@SessionAttribute("reviewRequestCode") String reviewRequestCode) {
highlightService.highlight(request);
return ResponseEntity.ok().build();
Copy link
Contributor

Choose a reason for hiding this comment

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

자원 개념으로 보았을 때 새로운 자원이 생성되었는데 created가 아닌 ok를 쓴 이유가 궁금해요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

생성 뿐만 아니라 수정 또한 이 API에서 일어납니다~

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package reviewme.highlight.service;

import org.springframework.stereotype.Service;
import reviewme.highlight.service.dto.HighlightsRequest;

@Service
public class HighlightService {

public void highlight(HighlightsRequest request) {
// TODO: implement method
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package reviewme.highlight.service.dto;

import jakarta.validation.constraints.NotNull;

public record HighlightIndexRangeRequest(

@NotNull(message = "시작 인덱스를 입력해주세요.")
Long startIndex,

@NotNull(message = "끝 인덱스를 입력해주세요.")
Long endIndex
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package reviewme.highlight.service.dto;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;

public record HighlightRequest(

@NotNull(message = "답변 ID를 입력해주세요.")
Long answerId,

@Valid @NotEmpty(message = "하이라이트 된 라인을 입력해주세요.")
List<HighlightedLineRequest> lines
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
Contributor Author

Choose a reason for hiding this comment

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

위 답변과 같아요~!

Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분은 빈리스트를 못받게 해야하지 않을까요?
왜냐면 answerId가 있는 request의 경우 lines가 빈 리스트가 올 수 없으니까요!
answerId가 있다 → 하이라이트가 있는 부분이어서 요청에 포함되었다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

최외곽 하나가 비어있다면 그냥 무시하고, 내부로 한 번 들어가기만 하면 매핑이 되어야한다는 이야기네요. 아래 질문 내용과 함께 반영할게요 🚀

Copy link
Contributor

Choose a reason for hiding this comment

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

댓츠 롸잇👍

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package reviewme.highlight.service.dto;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;

public record HighlightedLineRequest(

@NotNull(message = "인덱스를 입력해주세요.")
Long index,
Copy link
Contributor

Choose a reason for hiding this comment

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

(나머지 request도 동일하게) long 타입이어도 되지 않을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

request라 null이 들어올 수 있슴다~!

Copy link
Contributor

Choose a reason for hiding this comment

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

아차차😂


@Valid @NotEmpty(message = "하이라이트 범위를 입력해주세요.")
List<HighlightIndexRangeRequest> ranges
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package reviewme.highlight.service.dto;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.List;

public record HighlightsRequest(

@NotNull(message = "질문 ID를 입력해주세요.")
Long questionId,

@Valid @NotNull(message = "하이라이트할 부분을 입력해주세요.")
List<HighlightRequest> highlights
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
Contributor Author

Choose a reason for hiding this comment

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

삭제 요청을 같은 API에서 하니 빈 리스트도 괜찮지 않을까? 라는 생각입니다

Copy link
Contributor

Choose a reason for hiding this comment

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

삭제 요청을 같은 API에서 하니 빈 리스트도 괜찮지 않을까?

어떤 뜻인지 좀 더 설명해줄 수 있나요?
프론트에서 보낸다면 절대 빈 리스트가 들어올 수 없는데, 빈 리스트가 들어왔다면 잘못된 형식의 요청이니 dto에서 막는 게 맞다고 생각해서요!

Copy link
Contributor Author

@donghoony donghoony Oct 9, 2024

Choose a reason for hiding this comment

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

요청으로 항상 덮어쓴다고 생각하면 편할 듯 합니다 😁 빈 리스트가 들어온다면 하이라이트를 하나도 안 칠한 경우예요

) {
}
8 changes: 7 additions & 1 deletion backend/src/test/java/reviewme/api/ApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import reviewme.highlight.controller.HighlightController;
import reviewme.highlight.service.HighlightService;
import reviewme.review.controller.ReviewController;
import reviewme.review.service.GatheredReviewLookupService;
import reviewme.review.service.ReviewDetailLookupService;
Expand All @@ -44,7 +46,8 @@
ReviewGroupController.class,
ReviewController.class,
TemplateController.class,
SectionController.class
SectionController.class,
HighlightController.class
})
@ExtendWith(RestDocumentationExtension.class)
public abstract class ApiTest {
Expand Down Expand Up @@ -78,6 +81,9 @@ public abstract class ApiTest {
@MockBean
protected GatheredReviewLookupService gatheredReviewLookupService;

@MockBean
protected HighlightService highlightService;

Filter sessionCookieFilter = (request, response, chain) -> {
chain.doFilter(request, response);
HttpSession session = ((HttpServletRequest) request).getSession(false);
Expand Down
62 changes: 62 additions & 0 deletions backend/src/test/java/reviewme/api/HighlightApiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package reviewme.api;

import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;

import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.restdocs.cookies.CookieDescriptor;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.payload.FieldDescriptor;

class HighlightApiTest extends ApiTest {

@Test
void 존재하는_답변에_하이라이트를_생성한다() {
String request = """
{
"questionId": 1,
"highlights": [{
"answerId": 3,
"lines": [{
"index": 5,
"ranges": [{
"startIndex": 6,
"endIndex": 9
}]
}]
}]
}
""";

CookieDescriptor[] cookieDescriptors = {
cookieWithName("JSESSIONID").description("세션 ID")
};

FieldDescriptor[] requestFields = {
fieldWithPath("questionId").description("질문 ID"),
fieldWithPath("highlights").description("하이라이트 목록"),
fieldWithPath("highlights[].answerId").description("답변 ID"),
fieldWithPath("highlights[].lines[].index").description("개행으로 구분되는 라인 번호, 0-based"),
fieldWithPath("highlights[].lines[].ranges[].startIndex").description("하이라이트 시작 인덱스, 0-based"),
fieldWithPath("highlights[].lines[].ranges[].endIndex").description("하이라이트 끝 인덱스, 0-based")
};

RestDocumentationResultHandler handler = document(
"highlight-answer",
requestFields(requestFields),
requestCookies(cookieDescriptors)
);

givenWithSpec().log().all()
.cookie("JSESSIONID", "AVEBNKLCL13TNVZ")
.body(request)
.when().post("/v2/highlight")
.then().log().all()
.apply(handler)
.status(HttpStatus.OK);
}
}