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

TOP-90 feature Refresh Tht Access Token API 적용 #113

Merged
merged 5 commits into from
Dec 28, 2023
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
29 changes: 19 additions & 10 deletions app/src/main/java/com/tht/tht/SplashActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.tht.tht.databinding.ActivitySplashBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import tht.core.navigation.HomeNavigation
import tht.core.navigation.SignupNavigation
import tht.core.ui.delegate.viewBinding
import javax.inject.Inject
Expand All @@ -25,7 +27,7 @@ class SplashActivity : AppCompatActivity() {
lateinit var signupNavigation: SignupNavigation

@Inject
lateinit var homeNavigation: SignupNavigation
lateinit var homeNavigation: HomeNavigation

override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
Expand All @@ -52,16 +54,23 @@ class SplashActivity : AppCompatActivity() {

private fun observeViewModel() {
lifecycleScope.launch {
viewModel.sideEffect.collect {
when (it) {
is SplashSideEffect.Signup -> {
signupNavigation.navigatePreLogin(this@SplashActivity)
finish()
}
launch {
viewModel.loading.collect {
binding.progress.isVisible = it
}
}
launch {
viewModel.sideEffect.collect {
when (it) {
is SplashSideEffect.Signup -> {
signupNavigation.navigatePreLogin(this@SplashActivity)
finish()
}

is SplashSideEffect.Home -> {
homeNavigation.navigatePreLogin(this@SplashActivity)
finish()
is SplashSideEffect.Home -> {
homeNavigation.navigateHome(this@SplashActivity)
finish()
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/tht/tht/ThtFcmService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.tht.tht

import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
import com.tht.tht.domain.login.usecase.RefreshFcmTokenLoginUseCase
import com.tht.tht.domain.token.token.RefreshFcmTokenUseCase
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
Expand All @@ -14,7 +14,7 @@ import javax.inject.Inject
class ThtFcmService : FirebaseMessagingService() {

@Inject
lateinit var refreshFcmTokenLoginUseCase: RefreshFcmTokenLoginUseCase
lateinit var refreshFcmTokenUseCase: RefreshFcmTokenUseCase

private val job = SupervisorJob()
private val serviceScope = CoroutineScope(job)
Expand All @@ -23,7 +23,7 @@ class ThtFcmService : FirebaseMessagingService() {
super.onNewToken(token)
Log.d("TAG", "onNewToken => $token")
serviceScope.launch {
refreshFcmTokenLoginUseCase(token)
refreshFcmTokenUseCase(token)
.onFailure {
it.printStackTrace()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ object THTApiConstant {
}

object User {
const val ACCESS_TOKEN_REFRESH = "users/login/refresh"

const val REPORT = "user/report"

const val BLOCK = "/user/block/{block-user-uuid}"
Expand Down
32 changes: 24 additions & 8 deletions data/src/main/java/com/tht/tht/data/di/UseCaseModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import com.tht.tht.domain.image.ImageRepository
import com.tht.tht.domain.image.RemoveImageUrlUseCase
import com.tht.tht.domain.image.UploadImageUseCase
import com.tht.tht.domain.login.repository.LoginRepository
import com.tht.tht.domain.login.usecase.RefreshFcmTokenLoginUseCase
import com.tht.tht.domain.login.usecase.LoginUseCase
import com.tht.tht.domain.signup.repository.LocationRepository
import com.tht.tht.domain.signup.repository.RegionCodeRepository
import com.tht.tht.domain.signup.repository.SignupRepository
Expand All @@ -20,6 +20,8 @@ import com.tht.tht.domain.token.repository.TokenRepository
import com.tht.tht.domain.token.token.CheckAndRefreshThtAccessTokenUseCase
import com.tht.tht.domain.token.token.CheckThtAccessTokenExpiredUseCase
import com.tht.tht.domain.token.token.FetchThtAccessTokenUseCase
import com.tht.tht.domain.token.token.RefreshFcmTokenUseCase
import com.tht.tht.domain.token.token.RefreshThtAccessTokenUseCase
import com.tht.tht.domain.topic.DailyTopicRepository
import com.tht.tht.domain.topic.FetchDailyTopicListUseCase
import com.tht.tht.domain.topic.SelectTopicUseCase
Expand Down Expand Up @@ -126,14 +128,28 @@ object UseCaseModule {
@Provides
fun provideCheckSignupStateUseCase(
repository: SignupRepository,
tokenRepository: TokenRepository,
loginRepository: LoginRepository
loginUseCase: LoginUseCase
): CheckLoginEnableUseCase = CheckLoginEnableUseCase(
repository,
loginUseCase
)

@Provides
fun provideLoginUseCase(
tokenRepository: TokenRepository,
loginRepository: LoginRepository
): LoginUseCase = LoginUseCase(
tokenRepository,
loginRepository
)

@Provides
fun provideRefreshThtAccessTokenUseCase(
tokenRepository: TokenRepository
): RefreshThtAccessTokenUseCase = RefreshThtAccessTokenUseCase(
tokenRepository
)

@Provides
fun provideRequestVerifyUseCase(
repository: SignupRepository,
Expand Down Expand Up @@ -177,11 +193,11 @@ object UseCaseModule {
FetchLocationByAddressUseCase(repository, dispatcher)

@Provides
fun provideRequestFcmTokenLoginUseCase(
fun provideRefreshFcmTokenUseCase(
tokenRepository: TokenRepository,
loginRepository: LoginRepository
): RefreshFcmTokenLoginUseCase =
RefreshFcmTokenLoginUseCase(tokenRepository, loginRepository)
): RefreshFcmTokenUseCase =
RefreshFcmTokenUseCase(tokenRepository, loginRepository)

@Provides
fun provideFetchRegionCodeUseCase(
Expand Down Expand Up @@ -224,11 +240,11 @@ object UseCaseModule {

@Provides
fun provideCheckAndRefreshThtAccessTokenUseCase(
refreshFcmTokenLoginUseCase: RefreshFcmTokenLoginUseCase,
refreshThtAccessTokenUseCase: RefreshThtAccessTokenUseCase,
checkThtAccessTokenExpiredUseCase: CheckThtAccessTokenExpiredUseCase
): CheckAndRefreshThtAccessTokenUseCase =
CheckAndRefreshThtAccessTokenUseCase(
refreshFcmTokenLoginUseCase,
refreshThtAccessTokenUseCase,
checkThtAccessTokenExpiredUseCase
)

Expand Down
7 changes: 7 additions & 0 deletions data/src/main/java/com/tht/tht/data/di/UserModule.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.tht.tht.data.di

import com.tht.tht.data.remote.datasource.user.AccessTokenRefreshDataSource
import com.tht.tht.data.remote.datasource.user.AccessTokenRefreshDataSourceImpl
import com.tht.tht.data.remote.datasource.user.UserBlockDataSource
import com.tht.tht.data.remote.datasource.user.UserBlockDataSourceImpl
import com.tht.tht.data.remote.datasource.user.UserHeartDataSource
Expand All @@ -17,6 +19,11 @@ import dagger.hilt.components.SingletonComponent
@InstallIn(SingletonComponent::class)
abstract class UserModule {

@Binds
abstract fun bindAccessTokenRefreshDataSource(
impl: AccessTokenRefreshDataSourceImpl
): AccessTokenRefreshDataSource

@Binds
abstract fun bindUserReportDataSource(impl: UserReportDataSourceImpl): UserReportDataSource

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.tht.tht.data.remote.service.image.ImageService
import com.tht.tht.data.remote.service.image.ImageServiceImpl
import com.tht.tht.data.remote.service.location.RegionCodeApi
import com.tht.tht.data.remote.service.topic.DailyTopicApiService
import com.tht.tht.data.remote.service.user.AccessTokenRefreshService
import com.tht.tht.data.remote.service.user.UserBlockApiService
import com.tht.tht.data.remote.service.user.UserDislikeApiService
import com.tht.tht.data.remote.service.user.UserHeartApiService
Expand Down Expand Up @@ -92,4 +93,10 @@ object ApiServiceModule {
fun provideUserDisLikeApiService(
@ThtAccessTokenRetrofit retrofit: Retrofit
): UserDislikeApiService = retrofit.create(UserDislikeApiService::class.java)

@Provides
@Singleton
fun provideAccessTokenRefreshService(
@ThtAccessTokenRetrofit retrofit: Retrofit
): AccessTokenRefreshService = retrofit.create(AccessTokenRefreshService::class.java)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package com.tht.tht.data.di.retrofit
import com.tht.tht.data.BuildConfig
import com.tht.tht.data.remote.retrofit.TokenRefreshAuthenticator
import com.tht.tht.data.remote.retrofit.header.HttpHeaderKey
import com.tht.tht.domain.login.usecase.RefreshFcmTokenLoginUseCase
import com.tht.tht.domain.token.model.TokenException
import com.tht.tht.domain.token.token.FetchThtAccessTokenUseCase
import com.tht.tht.domain.token.token.RefreshThtAccessTokenUseCase
import dagger.Lazy
import dagger.Module
import dagger.Provides
Expand All @@ -20,18 +21,22 @@ object OkHttpInterceptorModule {

@Provides
fun provideHeaderInterceptor(
fetchThtAccessTokenUseCase: FetchThtAccessTokenUseCase
fetchThtAccessTokenUseCase: Lazy<FetchThtAccessTokenUseCase>
): Interceptor = Interceptor { chain ->
val requestBuilder = chain.request().newBuilder()
.header(HttpHeaderKey.CONTENT_TYPE_HEADER_KEY, HttpHeaderKey.CONTENT_TYPE_HEADER_VALUE)
val accessToken = runBlocking { fetchThtAccessTokenUseCase().getOrNull() }
val accessToken = runBlocking { fetchThtAccessTokenUseCase.get().invoke().getOrNull() }
if (accessToken != null) {
requestBuilder.header(
HttpHeaderKey.AUTHORIZATION_HEADER_KEY,
"${HttpHeaderKey.BEARER_PREFIX} $accessToken"
)
}
chain.proceed(requestBuilder.build())
chain.proceed(requestBuilder.build()).also { response ->
when (response.code) {
500 -> throw TokenException.RefreshTokenExpiredException()
}
}
}

@Provides
Expand All @@ -43,6 +48,6 @@ object OkHttpInterceptorModule {

@Provides
fun provideTokenRefreshAuthenticator(
refreshFcmTokenLoginUseCase: Lazy<RefreshFcmTokenLoginUseCase>
): TokenRefreshAuthenticator = TokenRefreshAuthenticator(refreshFcmTokenLoginUseCase)
refreshThtAccessTokenUseCase: Lazy<RefreshThtAccessTokenUseCase>
): TokenRefreshAuthenticator = TokenRefreshAuthenticator(refreshThtAccessTokenUseCase)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.tht.tht.data.remote.datasource.user

import com.tht.tht.data.remote.response.user.AccessTokenRefreshResponse

interface AccessTokenRefreshDataSource {
suspend fun refreshAccessToken(): AccessTokenRefreshResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.tht.tht.data.remote.datasource.user

import com.tht.tht.data.di.IODispatcher
import com.tht.tht.data.remote.mapper.toUnwrap
import com.tht.tht.data.remote.response.user.AccessTokenRefreshResponse
import com.tht.tht.data.remote.service.user.AccessTokenRefreshService
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import javax.inject.Inject

class AccessTokenRefreshDataSourceImpl @Inject constructor(
private val accessTokenRefreshService: AccessTokenRefreshService,
@IODispatcher private val dispatcher: CoroutineDispatcher
) : AccessTokenRefreshDataSource {

override suspend fun refreshAccessToken(): AccessTokenRefreshResponse {
return withContext(dispatcher) {
accessTokenRefreshService.refreshAccessToken().toUnwrap()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.tht.tht.data.remote.mapper

import com.tht.tht.data.remote.response.user.AccessTokenRefreshResponse
import com.tht.tht.domain.token.model.AccessTokenModel

fun AccessTokenRefreshResponse.toAccessTokenModel(): AccessTokenModel {
return AccessTokenModel(
accessToken = accessToken,
expiredTime = accessTokenExpiresIn
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.tht.tht.data.remote.response.user

import com.google.gson.annotations.SerializedName

data class AccessTokenRefreshResponse(
@SerializedName("accessToken")
val accessToken: String,
@SerializedName("accessTokenExpiresIn")
val accessTokenExpiresIn: Long
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.tht.tht.data.remote.retrofit

import com.tht.tht.domain.login.usecase.RefreshFcmTokenLoginUseCase
import com.tht.tht.domain.token.model.TokenException
import com.tht.tht.domain.token.token.RefreshThtAccessTokenUseCase
import dagger.Lazy
import kotlinx.coroutines.runBlocking
import okhttp3.Authenticator
Expand All @@ -11,7 +11,7 @@ import okhttp3.Route
import javax.inject.Inject

class TokenRefreshAuthenticator @Inject constructor(
private val refreshFcmTokenLoginUseCase: Lazy<RefreshFcmTokenLoginUseCase>
private val refreshFcmTokenUseCase: Lazy<RefreshThtAccessTokenUseCase>
) : Authenticator {

private val Response.retryCount: Int
Expand All @@ -28,7 +28,7 @@ class TokenRefreshAuthenticator @Inject constructor(
/**
* null 을 리턴 하면 본래 request 를 마저 수행
*/
override fun authenticate(route: Route?, response: Response): Request? {
override fun authenticate(route: Route?, response: Response): Request {
return when {
response.retryCount > (RETRY_COUNT - 1) -> null
response.code == 401 -> response.createRequest()
Expand All @@ -46,7 +46,7 @@ class TokenRefreshAuthenticator @Inject constructor(
}

private suspend fun reissueToken(): String? =
refreshFcmTokenLoginUseCase.get().invoke()
refreshFcmTokenUseCase.get().invoke()
.getOrNull()?.accessToken

private fun Request.retry(accessToken: String) = this
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.tht.tht.data.remote.service.user

import com.tht.tht.data.constant.THTApiConstant
import com.tht.tht.data.remote.response.base.ThtResponse
import com.tht.tht.data.remote.response.user.AccessTokenRefreshResponse
import retrofit2.http.POST

interface AccessTokenRefreshService {
@POST(THTApiConstant.User.ACCESS_TOKEN_REFRESH)
suspend fun refreshAccessToken(): ThtResponse<AccessTokenRefreshResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package com.tht.tht.data.repository

import com.tht.tht.data.local.datasource.TokenDataSource
import com.tht.tht.data.local.mapper.toModel
import com.tht.tht.data.remote.datasource.user.AccessTokenRefreshDataSource
import com.tht.tht.data.remote.mapper.toAccessTokenModel
import com.tht.tht.domain.token.model.AccessTokenModel
import com.tht.tht.domain.token.repository.TokenRepository
import javax.inject.Inject

class TokenRepositoryImpl @Inject constructor(
private val refreshAccessTokenRefreshDataSource: AccessTokenRefreshDataSource,
private val tokenDataSource: TokenDataSource
) : TokenRepository {
override suspend fun fetchFcmToken(): String {
Expand All @@ -28,4 +31,8 @@ class TokenRepositoryImpl @Inject constructor(
override suspend fun fetchPhone(): String? {
return tokenDataSource.fetchPhone()
}

override suspend fun refreshAccessToken(): AccessTokenModel {
return refreshAccessTokenRefreshDataSource.refreshAccessToken().toAccessTokenModel()
}
}
Loading