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

[System] TokenRepository, TokenApi 분리 #103

Merged
merged 4 commits into from
Feb 20, 2024
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
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
Loading