UpdateUserFromGithubUseCase.kt
package com.gyleedev.githubsearch.domain.usecase
import com.gyleedev.githubsearch.domain.model.UserSyncResult
import com.gyleedev.githubsearch.domain.model.UserUpdateResult
import com.gyleedev.githubsearch.domain.repository.GitHubRepository
import java.time.Clock
import java.time.Instant
import javax.inject.Inject
private const val ACCESS_TIMEOUT_MS = 3_600_000L
// TODO fetch 상세가 바뀐거에 맞춰서 로직 수정할 것
/*
Detail 에서 유저정보를 호출해서 업데이트 하는 함수
accesstime 을 확인하여 캐싱
1. AccessTime 을 Local 에서 확인한다.
lastAccess 확인 캐싱 처리 결정 단계
case1 : 시간 캐싱 필요 -> 전체 싱크 시작
case2 : 시간 캐싱 필요x repo체크 기록x -> repo 싱크 시작
case3 : 시간 캐싱 필요x repo체크 기록o -> 싱크 x
case4 : lastAccess 가 없다 -> 잘못된 접근 -> user정보도 찾아서 user 삭제
위에서 로직이 추가될 부분은
1. fetch 결과가 없다,
- user 결과가 없다. -> 일단 놔둠(fail)
- repo 결과가 없다. -> 그냥 insert 건너 뛴다.
2. exception이 난다. -> 그대로 fail 리턴
여기까지 testable 하게 코드 수정하고 test code 짜기
// TODO 이하의 내용은 정리해서 나중에 상세를 정하여 refactoring
2. 유저, 레포 싱크단계 <- 병렬처리 안하기로
필요한 싱크 항목을 병렬로 sync 한다.
case1
성공 -> accessTime update
실패 or Exception -> 업데이트 하다가 끊긴다면? Local이 멈춘다면?
=> 둘다 업데이트가 필요할 때는 transaction 을 사용해서 싱크하도록 함.
//Repository 는 향후 RemoteMediator 를 통해서 캐싱처리 할거기 때문에 간략하게 캐싱한다.(추가 수정하지 않겠음)
현재 repository 관련 이슈(onConflict -> Replace 상태)
user 캐싱을 했는데 web에 기록이 없다 -> 존재하지 않게된 유저(탈퇴) [처리방법 고민]
- 일단은 예외처리(Exception 으로 Fail 처리)
- 장기적으로 리팩터링 lastAccess 에 탈퇴정보 추가, 탈퇴면 fetch 하지 않도록
*/
class UpdateUserFromGithubUseCase @Inject constructor(
private val repository: GitHubRepository,
private val clock: Clock,
) {
suspend operator fun invoke(id: String): UserUpdateResult = try {
// 1. AccessTime 을 Local 에서 확인한다.
val lastAccess = repository.getLastAccessById(id)
if (lastAccess == null) {
// case4 : lastAccess 가 없다 -> 잘못된 접근 -> user정보도 찾아서 user 삭제
repository.deleteUserById(githubId = id)
UserUpdateResult.Fail
} else {
val durationSinceLastAccess = Instant.now(clock).toEpochMilli() - lastAccess.accessTime.toEpochMilli()
val isTimeOut = durationSinceLastAccess >= ACCESS_TIMEOUT_MS
val isRepoNeedSync = !lastAccess.isRepoFetched
when {
// case1 : 시간 캐싱 필요 -> 전체 싱크 시작
isTimeOut -> {
// user sync
val userSync = repository.syncUserData(id)
if (userSync is UserSyncResult.Success) {
// repo sync
// repo 결과가 없다 -> 그냥 insert 건너 뛴다. (repository 내부에서 처리)
repository.syncRepoDataList(entityId = userSync.entityId, githubId = id)
// accessTime update
repository.upsertAccessTime(id = lastAccess.id, githubId = id, isRepoFetched = true)
UserUpdateResult.Success
} else {
// user 결과가 없다. -> 일단 놔둠(fail)
UserUpdateResult.Fail
}
}
// case2 : 시간 캐싱 필요x repo체크 기록x -> repo 싱크 시작
isRepoNeedSync -> {
val entityId = repository.getUserId(id)
if (entityId != null) {
repository.syncRepoDataList(entityId, id)
}
repository.upsertAccessTime(id = lastAccess.id, githubId = id, isRepoFetched = true)
UserUpdateResult.Success
}
// case3 : 시간 캐싱 필요x repo체크 기록o -> 싱크 x
else -> {
UserUpdateResult.Success
}
}
}
} catch (e: Exception) {
// 2. exception이 난다. -> 그대로 fail 리턴
UserUpdateResult.Fail
}
}