Repository navigation
[feat] #222 - 구글 캘린더 일정을 투두로 저장 #228
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
Changes from all commits
d1516af
48ec0f3
ea7e903
1e0a4e5
6aced2a
3949d06
95cbddc
a09da6b
711ee7b
2df5fc7
02bd072
8ea33fe
6027d4c
3b027d2
9f1459c
1e193bf
2c4208c
3f7e0bf
9baa0d8
c1ea8df
8fd2222
7707184
eebd9d3
e132b8a
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,11 @@ | ||
| package com.Timo.Timo.domain.calendar.dto.request; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
|
|
||
| public record CalendarSyncRequest( | ||
| @Schema(description = "동기화 범위 필터 (DEFAULT: 기준일 ±7일, WEEK: 기준일부터 7일), 미입력 시 DEFAULT", example = "DEFAULT") | ||
| String filter, | ||
|
|
||
| @Schema(description = "기준 날짜 (yyyy-MM-dd), 미입력 시 오늘", example = "2026-10-09") | ||
| String baseDate | ||
| ) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| package com.Timo.Timo.domain.calendar.service; | ||
|
|
||
| import com.Timo.Timo.domain.calendar.client.GoogleOAuthClient; | ||
| import com.Timo.Timo.domain.calendar.dto.client.CalendarEventItem; | ||
| import com.Timo.Timo.domain.calendar.exception.CalendarErrorCode; | ||
| import com.Timo.Timo.domain.home.enums.HomeFilter; | ||
| import com.Timo.Timo.domain.user.entity.User; | ||
| import com.Timo.Timo.domain.user.exception.UserErrorCode; | ||
| import com.Timo.Timo.domain.user.repository.UserRepository; | ||
| import com.Timo.Timo.global.exception.CustomException; | ||
| import java.time.Instant; | ||
| import java.time.LocalDate; | ||
| import java.time.ZoneId; | ||
| import java.time.format.DateTimeParseException; | ||
| import java.util.List; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class CalendarEventSyncService { | ||
|
|
||
| private final UserRepository userRepository; | ||
| private final CalendarTokenService calendarTokenService; | ||
| private final GoogleOAuthClient googleOAuthClient; | ||
| private final CalendarTodoSynchronizer calendarTodoSynchronizer; | ||
|
|
||
| public void syncEvents(Long userId, String filterValue, String baseDateValue) { | ||
| User user = userRepository.findById(userId) | ||
| .orElseThrow(() -> new CustomException(UserErrorCode.USER_NOT_FOUND)); | ||
| ZoneId userZone = ZoneId.of(user.getZoneId()); | ||
|
|
||
| HomeFilter filter = parseFilter(filterValue); | ||
| LocalDate baseDate = parseBaseDate(baseDateValue, userZone); | ||
| LocalDate from = filter.rangeStart(baseDate); | ||
| LocalDate to = filter.rangeEnd(baseDate); | ||
|
|
||
| String accessToken = calendarTokenService.ensureValidAccessToken(userId); | ||
|
|
||
| Instant timeMin = from.atStartOfDay(userZone).toInstant(); | ||
| Instant timeMax = to.plusDays(1).atStartOfDay(userZone).toInstant(); | ||
| List<CalendarEventItem> items = googleOAuthClient.fetchEvents(accessToken, timeMin, timeMax); | ||
|
Collaborator
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. 구글 API 호출은 트랜잭션 밖에서 하고 저장만 |
||
|
|
||
| calendarTodoSynchronizer.synchronize(userId, items, from, to, userZone); | ||
| } | ||
|
|
||
| private HomeFilter parseFilter(String filterValue) { | ||
|
Collaborator
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. p4) HomeFilter.from과 같은 로직이 한 번 더 구현되어 있는 것 같습니다. 차이는 에러 코드뿐인 것 같아보이네요. parseBaseDate와 구글 호출 범위를 계산하는 부분도 CalendarEventQueryService와 같아서, 기존 GET /events를 지울 때 함께 정리하면 좋을 것 같습니다.
Member
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. 맞습니다. 말씀 주신대로 이번 PR에서는 그대로 두고, 기존 조회 API |
||
| if (filterValue == null || filterValue.isBlank()) { | ||
| return HomeFilter.DEFAULT; | ||
| } | ||
| try { | ||
| return HomeFilter.valueOf(filterValue); | ||
| } catch (IllegalArgumentException exception) { | ||
| throw new CustomException(CalendarErrorCode.INVALID_FILTER_OR_DATE); | ||
| } | ||
| } | ||
|
|
||
| private LocalDate parseBaseDate(String baseDateValue, ZoneId userZone) { | ||
| if (baseDateValue == null || baseDateValue.isBlank()) { | ||
| return LocalDate.now(userZone); | ||
| } | ||
| try { | ||
| return LocalDate.parse(baseDateValue); | ||
| } catch (DateTimeParseException exception) { | ||
| throw new CustomException(CalendarErrorCode.INVALID_FILTER_OR_DATE); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,10 +7,8 @@ | |
| import com.Timo.Timo.domain.calendar.dto.client.GoogleTokenResponse; | ||
| import com.Timo.Timo.domain.calendar.dto.client.GoogleUserInfoResponse; | ||
| import com.Timo.Timo.domain.calendar.entity.CalendarConnection; | ||
| import com.Timo.Timo.domain.calendar.entity.CalendarRevocationOutbox; | ||
| import com.Timo.Timo.domain.calendar.exception.CalendarErrorCode; | ||
| import com.Timo.Timo.domain.calendar.repository.CalendarConnectionRepository; | ||
| import com.Timo.Timo.domain.calendar.repository.CalendarRevocationOutboxRepository; | ||
| import com.Timo.Timo.domain.user.entity.User; | ||
| import com.Timo.Timo.domain.user.exception.UserErrorCode; | ||
| import com.Timo.Timo.domain.user.repository.UserRepository; | ||
|
|
@@ -19,6 +17,8 @@ | |
| import java.net.URLEncoder; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.time.Duration; | ||
| import java.time.LocalDate; | ||
| import java.time.ZoneId; | ||
| import java.util.List; | ||
| import java.util.UUID; | ||
| import lombok.RequiredArgsConstructor; | ||
|
|
@@ -44,7 +44,6 @@ public class CalendarService { | |
| private final CalendarConnectionCommandService commandService; | ||
| private final StringRedisTemplate redisTemplate; | ||
| private final CalendarStateValidator calendarStateValidator; | ||
| private final CalendarRevocationOutboxRepository calendarRevocationOutboxRepository; | ||
|
|
||
| @Value("${spring.security.oauth2.client.registration.google.client-id}") | ||
| private String clientId; | ||
|
|
@@ -113,17 +112,8 @@ public CalendarDisconnectResponse disconnect(Long userId) { | |
| CalendarConnection calendarConnection = calendarConnectionRepository.findByUserId(userId) | ||
| .orElseThrow(() -> new CustomException(CalendarErrorCode.CALENDAR_NOT_CONNECTED)); | ||
|
|
||
| String tokenToRevoke = calendarConnection.getRefreshToken() != null | ||
| ? calendarConnection.getRefreshToken() | ||
| : calendarConnection.getAccessToken(); | ||
|
|
||
| calendarRevocationOutboxRepository.save( | ||
| CalendarRevocationOutbox.builder() | ||
| .token(tokenToRevoke) | ||
| .build() | ||
| ); | ||
|
|
||
| commandService.deleteConnection(calendarConnection); | ||
| ZoneId userZone = ZoneId.of(calendarConnection.getUser().getZoneId()); | ||
| commandService.deleteConnection(calendarConnection, LocalDate.now(userZone)); | ||
|
Collaborator
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. p3) 그러면 토큰은 revoke되는데 연결은 남아서 사용자는 연동된 상태로 보이는데 동기화는 401이 나는 상황이 될 수 있을 것 같아요. 기존 순서라 이번 PR 범위 밖일 수도 있는데 한 번 봐주시면 좋을 것 같아용 (제가 틀릴수도... ㅎㅎ)
Member
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. 아주 날카로운 지적이셨습니다ㅎㅎ outbox 저장을 |
||
|
|
||
| return CalendarDisconnectResponse.builder() | ||
| .calendarConnected(false) | ||
|
|
||
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.
이제 투두 정리, revoke 기록, 연결 삭제가 같이 커밋되고 같이 롤백되네용 굿입니당!