-
Notifications
You must be signed in to change notification settings - Fork 5
[Feat/OPS-379] liveblocks 연동 #124
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
Merged
The head ref may contain hidden characters: "feat/OPS-379-BE-feat-liveblocks-\uC5F0\uB3D9"
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
47aee77
new : Liveblock API 호출을 위한 Client 빈 생성
f074ed7
feat : 스페이스 생성/삭제 시 liveblocks room 도 함께 생성/삭제
137141e
fix : test 시 mock 빈 사용
af1998a
feat : jwt 발급 로직 구현
b5b3287
fix : 최신 사항 반영
a4a3392
fix : SpaceArchiveDataSourceControllerTest에서 liveblocks 빈 mock 처리
034e32c
fix : 오타 수정
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
src/main/java/org/tuna/zoopzoop/backend/domain/dashboard/dto/ReqBodyForLiveblocksAuth.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package org.tuna.zoopzoop.backend.domain.dashboard.dto; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| public record ReqBodyForLiveblocksAuth( | ||
| String userId, | ||
| UserInfo userInfo, | ||
| Map<String, List<String>> permissions | ||
| ) { | ||
| public record UserInfo( | ||
| String name, | ||
| String avatar | ||
| ) {} | ||
| } |
5 changes: 5 additions & 0 deletions
5
src/main/java/org/tuna/zoopzoop/backend/domain/dashboard/dto/ResBodyForAuthToken.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package org.tuna.zoopzoop.backend.domain.dashboard.dto; | ||
|
|
||
| public record ResBodyForAuthToken( | ||
| String token | ||
| ){ } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
src/main/java/org/tuna/zoopzoop/backend/global/clients/liveblocks/LiveblocksClient.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,113 @@ | ||||||||||||||||||
| package org.tuna.zoopzoop.backend.global.clients.liveblocks; | ||||||||||||||||||
|
|
||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||
| import org.springframework.beans.factory.annotation.Value; | ||||||||||||||||||
| import org.springframework.http.*; | ||||||||||||||||||
| import org.springframework.stereotype.Component; | ||||||||||||||||||
| import org.springframework.web.client.RestClientException; | ||||||||||||||||||
| import org.springframework.web.client.RestTemplate; | ||||||||||||||||||
| import org.tuna.zoopzoop.backend.domain.dashboard.dto.ReqBodyForLiveblocksAuth; | ||||||||||||||||||
| import org.tuna.zoopzoop.backend.domain.dashboard.dto.ResBodyForAuthToken; | ||||||||||||||||||
|
|
||||||||||||||||||
| import java.util.Collections; | ||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||
|
|
||||||||||||||||||
| @Slf4j | ||||||||||||||||||
| @Component | ||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||
| public class LiveblocksClient { | ||||||||||||||||||
|
|
||||||||||||||||||
| private final RestTemplate restTemplate; | ||||||||||||||||||
|
|
||||||||||||||||||
| @Value("${liveblocks.secret-key}") | ||||||||||||||||||
| private String secretKey; | ||||||||||||||||||
|
|
||||||||||||||||||
| private static final String LIVEBLOCKS_API_URL = "https://api.liveblocks.io/v2/rooms"; | ||||||||||||||||||
| private static final String AUTH_API_URL = "https://api.liveblocks.io/v2/authorize-user"; | ||||||||||||||||||
| /** | ||||||||||||||||||
| * Liveblocks 서버에 새로운 방을 생성합니다. | ||||||||||||||||||
| * @param roomId 생성할 방의 고유 ID (워크스페이스 ID와 동일하게 사용) | ||||||||||||||||||
| */ | ||||||||||||||||||
| public void createRoom(String roomId) { | ||||||||||||||||||
| // 1. HTTP 헤더 설정 (Authorization: Bearer sk_...) | ||||||||||||||||||
| HttpHeaders headers = new HttpHeaders(); | ||||||||||||||||||
| headers.setBearerAuth(secretKey); | ||||||||||||||||||
| headers.setContentType(MediaType.APPLICATION_JSON); | ||||||||||||||||||
|
|
||||||||||||||||||
| // 2. Request Body 생성 (비공개 방으로 생성) | ||||||||||||||||||
| Map<String, Object> requestBody = new HashMap<>(); | ||||||||||||||||||
| requestBody.put("id", roomId); | ||||||||||||||||||
| requestBody.put("defaultAccesses", Collections.emptyList()); // 비공개(private) 방으로 설정 | ||||||||||||||||||
|
|
||||||||||||||||||
| // 3. HTTP 요청 엔티티 생성 | ||||||||||||||||||
| HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers); | ||||||||||||||||||
|
|
||||||||||||||||||
| try { | ||||||||||||||||||
| // 4. Liveblocks API에 POST 요청 전송 | ||||||||||||||||||
| ResponseEntity<String> response = restTemplate.postForEntity(LIVEBLOCKS_API_URL, requestEntity, String.class); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (response.getStatusCode().is2xxSuccessful()) { | ||||||||||||||||||
| log.info("Liveblocks room created successfully. roomId: {}", roomId); | ||||||||||||||||||
| } else { | ||||||||||||||||||
| log.error("Failed to create Liveblocks room. roomId: {}, status: {}, body: {}", | ||||||||||||||||||
| roomId, response.getStatusCode(), response.getBody()); | ||||||||||||||||||
| } | ||||||||||||||||||
| } catch (RestClientException e) { | ||||||||||||||||||
| log.error("Error while calling Liveblocks API to create room. roomId: {}", roomId, e); | ||||||||||||||||||
| // 필요하다면 여기서 커스텀 예외를 발생시켜 서비스 레이어에서 처리하도록 할 수 있습니다. | ||||||||||||||||||
| throw new RuntimeException("Liveblocks API call failed", e); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Liveblocks 서버의 방을 삭제합니다. | ||||||||||||||||||
| * @param roomId 삭제할 방의 고유 ID | ||||||||||||||||||
| */ | ||||||||||||||||||
| public void deleteRoom(String roomId) { | ||||||||||||||||||
| String deleteUrl = LIVEBLOCKS_API_URL + "/" + roomId; | ||||||||||||||||||
| HttpHeaders headers = new HttpHeaders(); | ||||||||||||||||||
| headers.setBearerAuth(secretKey); | ||||||||||||||||||
| HttpEntity<Void> requestEntity = new HttpEntity<>(headers); | ||||||||||||||||||
|
|
||||||||||||||||||
| try { | ||||||||||||||||||
| restTemplate.exchange(deleteUrl, HttpMethod.DELETE, requestEntity, Void.class); | ||||||||||||||||||
| log.info("Liveblocks room deleted successfully. roomId: {}", roomId); | ||||||||||||||||||
| } catch (RestClientException e) { | ||||||||||||||||||
| log.error("Error while calling Liveblocks API to delete room. roomId: {}", roomId, e); | ||||||||||||||||||
| // 방 삭제 실패가 전체 로직에 큰 영향을 주지 않는다면, | ||||||||||||||||||
| // 예외를 던지는 대신 에러 로그만 남기고 넘어갈 수도 있습니다. | ||||||||||||||||||
| // 여기서는 일단 예외를 던져서 트랜잭션을 롤백하도록 합니다. | ||||||||||||||||||
| throw new RuntimeException("Liveblocks API call failed", e); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Liveblocks 사용자 인증 토큰(JWT)을 발급받습니다. | ||||||||||||||||||
| * @param request 인증에 필요한 사용자 정보, 권한 등을 담은 객체 | ||||||||||||||||||
| * @return 발급된 JWT 문자열 | ||||||||||||||||||
| */ | ||||||||||||||||||
| public String getAuthToken(ReqBodyForLiveblocksAuth request) { | ||||||||||||||||||
| HttpHeaders headers = new HttpHeaders(); | ||||||||||||||||||
| headers.setBearerAuth(secretKey); | ||||||||||||||||||
| headers.setContentType(MediaType.APPLICATION_JSON); | ||||||||||||||||||
|
|
||||||||||||||||||
| HttpEntity<ReqBodyForLiveblocksAuth> requestEntity = new HttpEntity<>(request, headers); | ||||||||||||||||||
|
|
||||||||||||||||||
| try { | ||||||||||||||||||
| ResponseEntity<String> response = restTemplate.postForEntity(AUTH_API_URL, requestEntity, String.class); | ||||||||||||||||||
| if (response.getStatusCode().is2xxSuccessful()) { | ||||||||||||||||||
| log.info("Liveblocks auth token issued successfully for user: {}", request.userId()); | ||||||||||||||||||
| return response.getBody(); | ||||||||||||||||||
|
Comment on lines
+99
to
+102
|
||||||||||||||||||
| ResponseEntity<String> response = restTemplate.postForEntity(AUTH_API_URL, requestEntity, String.class); | |
| if (response.getStatusCode().is2xxSuccessful()) { | |
| log.info("Liveblocks auth token issued successfully for user: {}", request.userId()); | |
| return response.getBody(); | |
| ResponseEntity<ResBodyForAuthToken> response = restTemplate.postForEntity(AUTH_API_URL, requestEntity, ResBodyForAuthToken.class); | |
| if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) { | |
| log.info("Liveblocks auth token issued successfully for user: {}", request.userId()); | |
| return response.getBody().token(); |
14 changes: 14 additions & 0 deletions
14
src/main/java/org/tuna/zoopzoop/backend/global/config/restTemplate/RestTemplateConfig.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package org.tuna.zoopzoop.backend.global.config.restTemplate; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.web.client.RestTemplate; | ||
|
|
||
| @Configuration | ||
| public class RestTemplateConfig { | ||
|
|
||
| @Bean | ||
| public RestTemplate restTemplate() { | ||
| return new RestTemplate(); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The switch statement could be simplified using a more explicit mapping. Consider extracting the authority-to-permissions mapping into a separate method or using a Map for better maintainability.