-
Notifications
You must be signed in to change notification settings - Fork 1
[FEAT] 하트비트 및 인증로직 추가 #246
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
base: develop
Are you sure you want to change the base?
Changes from all commits
a840625
c4a088f
53dfa1a
0a75ec1
a12dd91
9ab2d02
2eca3d8
0821655
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.debatetimer.controller.sharing; | ||
|
|
||
| import com.debatetimer.controller.auth.AuthMember; | ||
| import com.debatetimer.domain.member.Member; | ||
| import com.debatetimer.dto.sharing.response.ChairmanTokenResponse; | ||
| import com.debatetimer.service.sharing.SharingService; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| public class SharingRestController { | ||
|
|
||
| private final SharingService sharingService; | ||
|
|
||
| @GetMapping("/api/share/{tableId}/chairman-token") | ||
| public ChairmanTokenResponse issueChairmanToken( | ||
| @AuthMember Member member, | ||
| @PathVariable("tableId") long tableId | ||
| ) { | ||
| return sharingService.issueChairmanToken(tableId, member); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,5 +3,6 @@ | |
| public enum TokenType { | ||
|
|
||
| ACCESS_TOKEN, | ||
| REFRESH_TOKEN | ||
| REFRESH_TOKEN, | ||
| CHAIRMAN_TOKEN | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.debatetimer.dto.sharing.response; | ||
|
|
||
| public record ChairmanTokenResponse( | ||
| String chairmanToken | ||
| ) { | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,6 +13,9 @@ public interface CustomizeTimeBoxRepository extends Repository<CustomizeTimeBoxE | |||||||||
|
|
||||||||||
| List<CustomizeTimeBoxEntity> findAllByCustomizeTable(CustomizeTableEntity table); | ||||||||||
|
|
||||||||||
| @Query("SELECT SUM(ctb.time) FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId") | ||||||||||
| long sumTimeByTableId(long tableId); | ||||||||||
|
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JPQL의
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 반영 예정
Comment on lines
+16
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: 네, JPQL/Hibernate에서 SUM(...) 집계 함수는 조건에 맞는 row가 0개일 때 null을 반환합니다. 이는 JPA 사양에 명시된 표준 동작입니다. Citations: 🏁 Script executed: # 파일 위치 확인
find . -name "CustomizeTimeBoxRepository.java" -type fRepository: debate-timer/debate-timer-be Length of output: 156 🏁 Script executed: # sumTimeByTableId 메서드 호출처 검색
rg "sumTimeByTableId" --type java -B 2 -A 2Repository: debate-timer/debate-timer-be Length of output: 2045 🏁 Script executed: # CustomizeTimeBoxRepository 파일 내용 확인
cat -n src/main/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepository.javaRepository: debate-timer/debate-timer-be Length of output: 1190 🏁 Script executed: # getTotalTimeBoxTimes 호출처 검색
rg "getTotalTimeBoxTimes" --type java -B 3 -A 3Repository: debate-timer/debate-timer-be Length of output: 1815 🏁 Script executed: # 토큰 생성/발급 관련 로직과의 연결 확인
rg "token|Token" --type java -i | grep -i "customize\|TimeBox" | head -20Repository: debate-timer/debate-timer-be Length of output: 1979 🏁 Script executed: # 테스트 코드 전체 확인 - empty 케이스 있는지 확인
cat -n src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java | head -80Repository: debate-timer/debate-timer-be Length of output: 4474 🏁 Script executed: # sumTimeByTableId 테스트 부분 확인
rg "sumTimeByTableId" src/test/java/com/debatetimer/repository/customize/CustomizeTimeBoxRepositoryTest.java -A 15 -B 5Repository: debate-timer/debate-timer-be Length of output: 598 🏁 Script executed: # issueChairmanToken 메서드 전체 확인
cat -n src/main/java/com/debatetimer/service/sharing/SharingService.java | grep -A 10 "issueChairmanToken"Repository: debate-timer/debate-timer-be Length of output: 586 🏁 Script executed: # empty 케이스 테스트 존재 여부 확인
rg "sumTimeByTableId|getTotalTimeBoxTimes" src/test -A 10 -B 5Repository: debate-timer/debate-timer-be Length of output: 1555
매칭 데이터가 없을 때 🔧 제안하는 수정-@Query("SELECT SUM(ctb.time) FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId")
+@Query("SELECT COALESCE(SUM(ctb.time), 0) FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId")
long sumTimeByTableId(long tableId);🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| @Query("DELETE FROM CustomizeTimeBoxEntity ctb WHERE ctb.customizeTable.id = :tableId") | ||||||||||
| @Modifying(clearAutomatically = true, flushAutomatically = true) | ||||||||||
| void deleteAllByTable(long tableId); | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,15 +1,26 @@ | ||||||||||||||||||||||||||||||||
| package com.debatetimer.service.sharing; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import com.debatetimer.controller.tool.jwt.JwtTokenProvider; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.domain.customize.CustomizeTable; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.domain.member.Member; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.domain.sharing.TimerEvent; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.domainrepository.customize.CustomizeTableDomainRepository; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.dto.member.MemberInfo; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.dto.sharing.request.SharingRequest; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.dto.sharing.response.ChairmanTokenResponse; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.dto.sharing.response.SharingResponse; | ||||||||||||||||||||||||||||||||
| import com.debatetimer.dto.sharing.response.TimerEventDataResponse; | ||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||
| public class SharingService { | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| private final JwtTokenProvider jwtTokenProvider; | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저희 이거 예전에 Oauth 로그인 할 때 AuthManager로 Controller 단에서 처리하기로 합의하지 않았었나요? |
||||||||||||||||||||||||||||||||
| private final CustomizeTableDomainRepository customizeTableDomainRepository; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public SharingResponse share(SharingRequest request) { | ||||||||||||||||||||||||||||||||
| TimerEvent timerEvent = request.toTimerEvent(); | ||||||||||||||||||||||||||||||||
| return Optional.ofNullable(timerEvent.getTimerEventData()) | ||||||||||||||||||||||||||||||||
|
|
@@ -19,4 +30,11 @@ public SharingResponse share(SharingRequest request) { | |||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||
| .orElse(new SharingResponse(request.eventType(), null)); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| public ChairmanTokenResponse issueChairmanToken(long tableId, Member member) { | ||||||||||||||||||||||||||||||||
| CustomizeTable customizeTable = customizeTableDomainRepository.getByIdAndMember(tableId, member); | ||||||||||||||||||||||||||||||||
| long debateTime = customizeTableDomainRepository.getTotalTimeBoxTimes(customizeTable.getId()); | ||||||||||||||||||||||||||||||||
| String chairmanToken = jwtTokenProvider.createChairmanToken(new MemberInfo(member), debateTime * 2); | ||||||||||||||||||||||||||||||||
| return new ChairmanTokenResponse(chairmanToken); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 타임박스가 없을 경우 토큰 만료 시간이 0이 됨
최소 만료 시간을 설정하거나, 타임박스가 없는 경우에 대한 처리를 고려해 주세요. 🛡️ 최소 만료 시간 적용 제안 public ChairmanTokenResponse issueChairmanToken(long tableId, Member member) {
+ private static final long MINIMUM_EXPIRATION_SECONDS = 300; // 최소 5분
+
CustomizeTable customizeTable = customizeTableDomainRepository.getByIdAndMember(tableId, member);
long debateTime = customizeTableDomainRepository.getTotalTimeBoxTimes(customizeTable.getId());
- String chairmanToken = jwtTokenProvider.createChairmanToken(new MemberInfo(member), debateTime * 2);
+ long expirationSeconds = Math.max(debateTime * 2, MINIMUM_EXPIRATION_SECONDS);
+ String chairmanToken = jwtTokenProvider.createChairmanToken(new MemberInfo(member), expirationSeconds);
return new ChairmanTokenResponse(chairmanToken);
}또는 타임박스가 없는 테이블에 대해서는 예외를 발생시키는 방법도 있습니다: if (debateTime == 0) {
throw new DTClientErrorException(ClientErrorCode.NO_TIME_BOXES_CONFIGURED);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이미 타임박스가 없는 경우는 도메인적으로 제한하고 있어 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 8190 🏁 Script executed: Length of output: 0 🏁 Script executed: Length of output: 7461 🏁 Script executed: Length of output: 2613 🏁 Script executed: Length of output: 5082
다만 코드를 살펴보면서 한 가지 추가로 확인하실 사항이 있습니다.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @coli-geonwoo 확인 해주세요 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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,59 @@ | ||||||||||||||||
| package com.debatetimer.controller.sharing; | ||||||||||||||||
|
|
||||||||||||||||
| import static org.mockito.ArgumentMatchers.any; | ||||||||||||||||
| import static org.mockito.ArgumentMatchers.eq; | ||||||||||||||||
| import static org.mockito.Mockito.doReturn; | ||||||||||||||||
| import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; | ||||||||||||||||
| import static org.springframework.restdocs.payload.JsonFieldType.STRING; | ||||||||||||||||
| import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; | ||||||||||||||||
| import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; | ||||||||||||||||
|
|
||||||||||||||||
| import com.debatetimer.controller.BaseDocumentTest; | ||||||||||||||||
| import com.debatetimer.controller.RestDocumentationRequest; | ||||||||||||||||
| import com.debatetimer.controller.RestDocumentationResponse; | ||||||||||||||||
| import com.debatetimer.controller.Tag; | ||||||||||||||||
| import com.debatetimer.domain.member.Member; | ||||||||||||||||
| import com.debatetimer.dto.sharing.response.ChairmanTokenResponse; | ||||||||||||||||
| import io.restassured.http.ContentType; | ||||||||||||||||
| import org.junit.jupiter.api.Nested; | ||||||||||||||||
| import org.junit.jupiter.api.Test; | ||||||||||||||||
| import org.springframework.http.HttpHeaders; | ||||||||||||||||
|
|
||||||||||||||||
| public class SharingDocumentTest extends BaseDocumentTest { | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| @Nested | ||||||||||||||||
|
Comment on lines
+22
to
+25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||
| class IssueChairmanToken { | ||||||||||||||||
|
|
||||||||||||||||
| private final RestDocumentationRequest requestDocument = request() | ||||||||||||||||
| .tag(Tag.SHARING_API) | ||||||||||||||||
| .summary("사회자용 토큰 발급") | ||||||||||||||||
| .requestHeader(headerWithName(HttpHeaders.AUTHORIZATION).description("액세스 토큰")) | ||||||||||||||||
| .pathParameter(parameterWithName("tableId").description("테이블 id")); | ||||||||||||||||
|
|
||||||||||||||||
| private final RestDocumentationResponse responseDocument = response() | ||||||||||||||||
| .responseBodyField( | ||||||||||||||||
| fieldWithPath("chairmanToken").type(STRING).description("사회자용 토큰") | ||||||||||||||||
| ); | ||||||||||||||||
|
|
||||||||||||||||
| @Test | ||||||||||||||||
| void 사회자_용_토큰_생성_성공() { | ||||||||||||||||
| long requestTableId = 1L; | ||||||||||||||||
| ChairmanTokenResponse chairmanTokenResponse = new ChairmanTokenResponse("testToken"); | ||||||||||||||||
| doReturn(chairmanTokenResponse).when(sharingService) | ||||||||||||||||
| .issueChairmanToken(eq(requestTableId), any(Member.class)); | ||||||||||||||||
|
|
||||||||||||||||
| var document = document("sharing/get", 200) | ||||||||||||||||
| .request(requestDocument) | ||||||||||||||||
| .response(responseDocument) | ||||||||||||||||
| .build(); | ||||||||||||||||
|
|
||||||||||||||||
| given(document) | ||||||||||||||||
| .contentType(ContentType.JSON) | ||||||||||||||||
| .headers(EXIST_MEMBER_HEADER) | ||||||||||||||||
| .pathParam("tableId", String.valueOf(requestTableId)) | ||||||||||||||||
| .when().get("/api/share/{tableId}/chairman-token") | ||||||||||||||||
| .then().statusCode(200); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.