Skip to content

Commit

Permalink
[System] TokenRepository, TokenApi 분리 (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajou4095 authored Feb 20, 2024
1 parent 9f6ddaf commit f734ca8
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 139 deletions.
2 changes: 1 addition & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# TODO : Proguard 설정
-dontwarn org.slf4j.**
7 changes: 4 additions & 3 deletions app/src/main/kotlin/ac/dnd/mour/android/MourApplication.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ac.dnd.mour.android

import ac.dnd.mour.android.domain.repository.AuthenticationRepository
import ac.dnd.mour.android.domain.repository.TokenRepository
import ac.dnd.mour.android.presentation.common.CHANNEL_1
import ac.dnd.mour.android.presentation.common.CHANNEL_GROUP_1
import ac.dnd.mour.android.presentation.ui.invalid.InvalidJwtTokenActivity
Expand Down Expand Up @@ -31,7 +31,7 @@ import timber.log.Timber
open class MourApplication : Application() {

@Inject
lateinit var authenticationRepository: AuthenticationRepository
lateinit var tokenRepository: TokenRepository

private val handler = CoroutineExceptionHandler { _, exception ->
Timber.d(exception)
Expand Down Expand Up @@ -84,7 +84,7 @@ open class MourApplication : Application() {
with(ProcessLifecycleOwner.get()) {
lifecycleScope.launch(handler) {
repeatOnLifecycle(Lifecycle.State.STARTED) {
authenticationRepository.isRefreshTokenInvalid.collect { isRefreshTokenInvalid ->
tokenRepository.isRefreshTokenInvalid.collect { isRefreshTokenInvalid ->
if (isRefreshTokenInvalid) {
val intent = Intent(
this@MourApplication,
Expand All @@ -94,6 +94,7 @@ open class MourApplication : Application() {
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(intent)
tokenRepository.resetRefreshTokenInvalidFlag()
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion data/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package ac.dnd.mour.android.data.di

import ac.dnd.mour.android.data.remote.local.gallery.GalleryImageRepositoryImpl
import ac.dnd.mour.android.data.repository.authentication.MockAuthenticationRepository
import ac.dnd.mour.android.data.repository.authentication.RealAuthenticationRepository
import ac.dnd.mour.android.data.repository.authentication.sociallogin.KakaoLoginRepositoryImpl
import ac.dnd.mour.android.data.repository.feature.group.MockGroupRepository
import ac.dnd.mour.android.data.repository.feature.heart.MockHeartRepository
import ac.dnd.mour.android.data.repository.authentication.token.RealTokenRepository
import ac.dnd.mour.android.data.repository.feature.group.RealGroupRepository
import ac.dnd.mour.android.data.repository.feature.heart.RealHeartRepository
import ac.dnd.mour.android.data.repository.feature.relation.KakaoFriendRepositoryImpl
import ac.dnd.mour.android.data.repository.feature.relation.MockRelationRepository
import ac.dnd.mour.android.data.repository.feature.schedule.MockScheduleRepository
import ac.dnd.mour.android.data.repository.feature.statistics.MockStatisticsRepository
import ac.dnd.mour.android.data.repository.file.MockFileRepository
import ac.dnd.mour.android.data.repository.feature.relation.RealRelationRepository
import ac.dnd.mour.android.data.repository.feature.schedule.RealScheduleRepository
import ac.dnd.mour.android.data.repository.feature.statistics.RealStatisticsRepository
import ac.dnd.mour.android.data.repository.file.RealFileRepository
import ac.dnd.mour.android.data.repository.gallery.GalleryRepositoryImpl
import ac.dnd.mour.android.data.repository.member.MockMemberRepository
import ac.dnd.mour.android.data.repository.member.RealMemberRepository
import ac.dnd.mour.android.domain.repository.AuthenticationRepository
import ac.dnd.mour.android.domain.repository.FileRepository
import ac.dnd.mour.android.domain.repository.GalleryImageRepository
Expand All @@ -24,6 +25,7 @@ import ac.dnd.mour.android.domain.repository.MemberRepository
import ac.dnd.mour.android.domain.repository.RelationRepository
import ac.dnd.mour.android.domain.repository.ScheduleRepository
import ac.dnd.mour.android.domain.repository.StatisticsRepository
import ac.dnd.mour.android.domain.repository.TokenRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
Expand All @@ -34,46 +36,52 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
internal abstract class RepositoryModule {

@Binds
@Singleton
abstract fun bindsTokenRepository(
tokenRepository: RealTokenRepository
): TokenRepository

@Binds
@Singleton
abstract fun bindsAuthenticationRepository(
authenticationRepository: MockAuthenticationRepository
authenticationRepository: RealAuthenticationRepository
): AuthenticationRepository

@Binds
@Singleton
abstract fun bindsGroupRepository(
groupRepository: MockGroupRepository
groupRepository: RealGroupRepository
): GroupRepository

@Binds
@Singleton
abstract fun bindsHeartRepository(
heartRepository: MockHeartRepository
heartRepository: RealHeartRepository
): HeartRepository

@Binds
@Singleton
abstract fun bindsMemberRepository(
memberRepository: MockMemberRepository
memberRepository: RealMemberRepository
): MemberRepository

@Binds
@Singleton
abstract fun bindsRelationRepository(
relationRepository: MockRelationRepository
relationRepository: RealRelationRepository
): RelationRepository

@Binds
@Singleton
abstract fun bindsScheduleRepository(
scheduleRepository: MockScheduleRepository
scheduleRepository: RealScheduleRepository
): ScheduleRepository

@Binds
@Singleton
abstract fun bindsStatisticsRepository(
statisticsRepository: MockStatisticsRepository
statisticsRepository: RealStatisticsRepository
): StatisticsRepository

@Binds
Expand All @@ -91,7 +99,7 @@ internal abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindsFileRepository(
fileRepository: MockFileRepository
fileRepository: RealFileRepository
): FileRepository

@Binds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import ac.dnd.mour.android.data.remote.network.di.AuthHttpClient
import ac.dnd.mour.android.data.remote.network.di.NoAuthHttpClient
import ac.dnd.mour.android.data.remote.network.environment.BaseUrlProvider
import ac.dnd.mour.android.data.remote.network.environment.ErrorMessageMapper
import ac.dnd.mour.android.data.remote.network.model.authentication.GetAccessTokenRes
import ac.dnd.mour.android.data.remote.network.model.authentication.LoginReq
import ac.dnd.mour.android.data.remote.network.model.authentication.LoginRes
import ac.dnd.mour.android.data.remote.network.model.authentication.RegisterReq
import ac.dnd.mour.android.data.remote.network.model.authentication.RegisterRes
import ac.dnd.mour.android.data.remote.network.util.convert
import io.ktor.client.HttpClient
import io.ktor.client.request.delete
import io.ktor.client.request.header
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import javax.inject.Inject
Expand All @@ -26,14 +24,6 @@ class AuthenticationApi @Inject constructor(
private val baseUrl: String
get() = baseUrlProvider.get()

suspend fun getAccessToken(
refreshToken: String
): Result<GetAccessTokenRes> {
return noAuthClient.post("$baseUrl/api/v1/token/reissue") {
header("Token-Refresh", refreshToken)
}.convert(errorMessageMapper::map)
}

suspend fun login(
socialId: Long,
email: String
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ac.dnd.mour.android.data.remote.network.api

import ac.dnd.mour.android.data.remote.network.di.NoAuthHttpClient
import ac.dnd.mour.android.data.remote.network.environment.BaseUrlProvider
import ac.dnd.mour.android.data.remote.network.environment.ErrorMessageMapper
import ac.dnd.mour.android.data.remote.network.model.authentication.GetAccessTokenRes
import ac.dnd.mour.android.data.remote.network.util.convert
import io.ktor.client.HttpClient
import io.ktor.client.request.header
import io.ktor.client.request.post
import javax.inject.Inject

class TokenApi @Inject constructor(
@NoAuthHttpClient private val noAuthClient: HttpClient,
private val baseUrlProvider: BaseUrlProvider,
private val errorMessageMapper: ErrorMessageMapper
) {
private val baseUrl: String
get() = baseUrlProvider.get()

suspend fun getAccessToken(
refreshToken: String
): Result<GetAccessTokenRes> {
return noAuthClient.post("$baseUrl/api/v1/token/reissue") {
header("Token-Refresh", refreshToken)
}.convert(errorMessageMapper::map)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ac.dnd.mour.android.data.remote.network.di

import ac.dnd.mour.android.domain.repository.AuthenticationRepository
import ac.dnd.mour.android.domain.repository.TokenRepository
import android.content.Context
import android.content.pm.ApplicationInfo
import dagger.Module
Expand Down Expand Up @@ -40,7 +40,7 @@ internal object KtorModule {
): HttpClient {
return HttpClient(OkHttp) {
// default validation to throw exceptions for non-2xx responses
expectSuccess = true
expectSuccess = false

engine {
if (debugInterceptor.isPresent) {
Expand All @@ -61,14 +61,13 @@ internal object KtorModule {
fun provideAuthHttpClient(
@ApplicationContext context: Context,
@DebugInterceptor debugInterceptor: Optional<Interceptor>,
authenticationRepository: AuthenticationRepository
tokenRepository: TokenRepository
): HttpClient {
val isDebug: Boolean =
(0 != context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE)

return HttpClient(OkHttp) {
// default validation to throw exceptions for non-2xx responses
// TODO
expectSuccess = false

engine {
Expand All @@ -84,8 +83,8 @@ internal object KtorModule {
install(Auth) {
bearer {
loadTokens {
val accessToken = authenticationRepository.accessToken
val refreshToken = authenticationRepository.refreshToken
val accessToken = tokenRepository.accessToken
val refreshToken = tokenRepository.refreshToken
if (accessToken.isEmpty() || refreshToken.isEmpty()) {
return@loadTokens null
}
Expand All @@ -97,12 +96,12 @@ internal object KtorModule {
}

refreshTokens {
val refreshToken = authenticationRepository.refreshToken
val refreshToken = tokenRepository.refreshToken
if (refreshToken.isEmpty()) {
return@refreshTokens null
}

authenticationRepository.refreshToken(
tokenRepository.refreshToken(
refreshToken
).getOrNull()?.let { token ->
BearerTokens(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,17 @@
package ac.dnd.mour.android.data.repository.authentication

import ac.dnd.mour.android.data.remote.local.SharedPreferencesManager
import ac.dnd.mour.android.domain.model.authentication.JwtToken
import ac.dnd.mour.android.domain.model.error.ServerException
import ac.dnd.mour.android.domain.model.legacy.Login
import ac.dnd.mour.android.domain.model.legacy.Register
import ac.dnd.mour.android.domain.repository.AuthenticationRepository
import ac.dnd.mour.android.domain.repository.TokenRepository
import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class MockAuthenticationRepository @Inject constructor(
private val sharedPreferencesManager: SharedPreferencesManager
private val tokenRepository: TokenRepository
) : AuthenticationRepository {

override var refreshToken: String
set(value) = sharedPreferencesManager.setString(REFRESH_TOKEN, value)
get() = sharedPreferencesManager.getString(REFRESH_TOKEN, "")

override var accessToken: String
set(value) = sharedPreferencesManager.setString(ACCESS_TOKEN, value)
get() = sharedPreferencesManager.getString(ACCESS_TOKEN, "")

private val _isRefreshTokenInvalid: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val isRefreshTokenInvalid: StateFlow<Boolean> = _isRefreshTokenInvalid.asStateFlow()

override suspend fun refreshToken(
refreshToken: String
): Result<JwtToken> {
randomShortDelay()
return Result.success(
JwtToken(
accessToken = "mock_access_token",
refreshToken = "mock_refresh_token"
)
)
}

override suspend fun login(
socialId: Long,
email: String
Expand Down Expand Up @@ -77,9 +50,4 @@ class MockAuthenticationRepository @Inject constructor(
private suspend fun randomLongDelay() {
delay(LongRange(500, 2000).random())
}

companion object {
private const val REFRESH_TOKEN = "mock_refresh_token"
private const val ACCESS_TOKEN = "mock_access_token"
}
}
Loading

0 comments on commit f734ca8

Please sign in to comment.