diff --git a/app/src/main/java/com/going/doorip/di/AuthInterceptor.kt b/app/src/main/java/com/going/doorip/di/AuthInterceptor.kt new file mode 100644 index 00000000..fe73f8b8 --- /dev/null +++ b/app/src/main/java/com/going/doorip/di/AuthInterceptor.kt @@ -0,0 +1,72 @@ +package com.going.doorip.di + +import android.content.Context +import com.going.data.dto.BaseResponse +import com.going.data.local.GoingDataStore +import com.going.domain.entity.response.AuthTokenModel +import com.going.doorip.BuildConfig.BASE_URL +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.serialization.json.Json +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import timber.log.Timber +import javax.inject.Inject + +class AuthInterceptor @Inject constructor( + private val json: Json, + private val dataStore: GoingDataStore, + @ApplicationContext private val context: Context, +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + + Timber.d("GET ACCESS TOKEN : ${dataStore.accessToken}") + + val authRequest = if (dataStore.accessToken.isNotBlank()) { + originalRequest.newAuthBuilder().build() + } else { + originalRequest + } + val response = chain.proceed(authRequest) + + when (response.code) { + CODE_TOKEN_EXPIRED -> { + try { + val refreshTokenRequest = originalRequest.newBuilder().post("".toRequestBody()) + .url("$BASE_URL/api/users/reissue") + .addHeader(AUTHORIZATION, dataStore.refreshToken) + .build() + val refreshTokenResponse = chain.proceed(refreshTokenRequest) + Timber.d("GET REFRESH TOKEN : $refreshTokenResponse") + + if (refreshTokenResponse.isSuccessful) { + val responseToken = json.decodeFromString( + refreshTokenResponse.body?.string().toString(), + ) as BaseResponse + + with(dataStore) { + accessToken = responseToken.data.accessToken + refreshToken = responseToken.data.refreshToken + } + refreshTokenResponse.close() + val newRequest = originalRequest.newAuthBuilder().build() + return chain.proceed(newRequest) + } + } catch (t: Throwable) { + Timber.e(t) + } + } + } + return response + } + + private fun Request.newAuthBuilder() = + this.newBuilder().addHeader(AUTHORIZATION, "Bearer ${dataStore.accessToken}") + + companion object { + private const val CODE_TOKEN_EXPIRED = 401 + private const val AUTHORIZATION = "Authorization" + } +} diff --git a/app/src/main/java/com/going/doorip/di/DataSourceModule.kt b/app/src/main/java/com/going/doorip/di/DataSourceModule.kt index a09e21b3..aadf4dd3 100644 --- a/app/src/main/java/com/going/doorip/di/DataSourceModule.kt +++ b/app/src/main/java/com/going/doorip/di/DataSourceModule.kt @@ -2,8 +2,10 @@ package com.going.doorip.di import com.going.data.datasource.AuthDataSource import com.going.data.datasource.MockDataSource +import com.going.data.datasource.SettingDataSource import com.going.data.datasourceImpl.AuthDataSourceImpl import com.going.data.datasourceImpl.MockDataSourceImpl +import com.going.data.datasourceImpl.SettingDataSourceImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -23,4 +25,9 @@ object DataSourceModule { @Singleton fun provideAuthDataSource(authDataSourceImpl: AuthDataSourceImpl): AuthDataSource = authDataSourceImpl + + @Provides + @Singleton + fun provideSettingDataSource(settingDataSourceImpl: SettingDataSourceImpl): SettingDataSource = + settingDataSourceImpl } diff --git a/app/src/main/java/com/going/doorip/di/DataStoreModule.kt b/app/src/main/java/com/going/doorip/di/DataStoreModule.kt index b3b4492c..41c93535 100644 --- a/app/src/main/java/com/going/doorip/di/DataStoreModule.kt +++ b/app/src/main/java/com/going/doorip/di/DataStoreModule.kt @@ -2,8 +2,8 @@ package com.going.doorip.di import android.content.Context import android.content.SharedPreferences -import com.going.data.local.GoingDataStoreImpl import com.going.data.local.GoingDataStore +import com.going.data.local.GoingDataStoreImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/going/doorip/di/RepositoryModule.kt b/app/src/main/java/com/going/doorip/di/RepositoryModule.kt index 7a46e6a8..db33b4cc 100644 --- a/app/src/main/java/com/going/doorip/di/RepositoryModule.kt +++ b/app/src/main/java/com/going/doorip/di/RepositoryModule.kt @@ -1,11 +1,13 @@ package com.going.doorip.di -import com.going.data.repositoryImpl.TokenRepositoryImpl import com.going.data.repositoryImpl.AuthRepositoryImpl import com.going.data.repositoryImpl.MockRepositoryImpl -import com.going.domain.repository.TokenRepository +import com.going.data.repositoryImpl.SettingRepositoryImpl +import com.going.data.repositoryImpl.TokenRepositoryImpl import com.going.domain.repository.AuthRepository import com.going.domain.repository.MockRepository +import com.going.domain.repository.SettingRepository +import com.going.domain.repository.TokenRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -30,4 +32,9 @@ object RepositoryModule { @Singleton fun provideTokenRepository(tokenRepositoryImpl: TokenRepositoryImpl): TokenRepository = tokenRepositoryImpl + + @Provides + @Singleton + fun provideSettingRepository(settingRepositoryImpl: SettingRepositoryImpl): SettingRepository = + settingRepositoryImpl } diff --git a/app/src/main/java/com/going/doorip/di/RetrofitModule.kt b/app/src/main/java/com/going/doorip/di/RetrofitModule.kt index 9b8c2b55..435739aa 100644 --- a/app/src/main/java/com/going/doorip/di/RetrofitModule.kt +++ b/app/src/main/java/com/going/doorip/di/RetrofitModule.kt @@ -1,6 +1,8 @@ package com.going.doorip.di import com.going.doorip.BuildConfig.BASE_URL +import com.going.going.di.AuthInterceptor +import com.going.going.di.qualifier.JWT import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module import dagger.Provides @@ -54,15 +56,22 @@ object RetrofitModule { @Provides @Singleton - fun provideOkHttpClient( + @JWT + fun provideAuthInterceptor(authInterceptor: AuthInterceptor): Interceptor = authInterceptor + + @Provides + @Singleton + fun provideJWTOkHttpClient( loggingInterceptor: Interceptor, + @JWT authInterceptor: Interceptor, ): OkHttpClient = OkHttpClient.Builder() .addInterceptor(loggingInterceptor) + .addInterceptor(authInterceptor) .build() @Provides @Singleton - fun provideRetrofit( + fun provideJWTRetrofit( client: OkHttpClient, factory: Converter.Factory, ): Retrofit = Retrofit.Builder() diff --git a/app/src/main/java/com/going/doorip/di/ServiceModule.kt b/app/src/main/java/com/going/doorip/di/ServiceModule.kt index 86c43ab7..bd88da51 100644 --- a/app/src/main/java/com/going/doorip/di/ServiceModule.kt +++ b/app/src/main/java/com/going/doorip/di/ServiceModule.kt @@ -2,6 +2,7 @@ package com.going.doorip.di import com.going.data.service.AuthService import com.going.data.service.MockService +import com.going.data.service.SettingService import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -20,6 +21,11 @@ object ServiceModule { @Provides @Singleton - fun provideLoginService(retrofit: Retrofit): AuthService = + fun provideAuthService(retrofit: Retrofit): AuthService = retrofit.create(AuthService::class.java) + + @Provides + @Singleton + fun provideSettingService(retrofit: Retrofit): SettingService = + retrofit.create(SettingService::class.java) } diff --git a/app/src/main/java/com/going/doorip/di/qualifier/RetrofitQualifier.kt b/app/src/main/java/com/going/doorip/di/qualifier/RetrofitQualifier.kt new file mode 100644 index 00000000..c4935bf2 --- /dev/null +++ b/app/src/main/java/com/going/doorip/di/qualifier/RetrofitQualifier.kt @@ -0,0 +1,7 @@ +package com.going.doorip.di.qualifier + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class JWT diff --git a/data/src/main/java/com/going/data/datasource/SettingDataSource.kt b/data/src/main/java/com/going/data/datasource/SettingDataSource.kt new file mode 100644 index 00000000..0b7cab84 --- /dev/null +++ b/data/src/main/java/com/going/data/datasource/SettingDataSource.kt @@ -0,0 +1,7 @@ +package com.going.data.datasource + +import com.going.data.dto.response.SignOutResponseDto + +interface SettingDataSource { + suspend fun patchSignOut(): SignOutResponseDto +} diff --git a/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt b/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt new file mode 100644 index 00000000..7e3444ca --- /dev/null +++ b/data/src/main/java/com/going/data/datasourceImpl/SettingDataSourceImpl.kt @@ -0,0 +1,12 @@ +package com.going.data.datasourceImpl + +import com.going.data.datasource.SettingDataSource +import com.going.data.dto.response.SignOutResponseDto +import com.going.data.service.SettingService +import javax.inject.Inject + +class SettingDataSourceImpl @Inject constructor( + private val settingService: SettingService, +) : SettingDataSource { + override suspend fun patchSignOut(): SignOutResponseDto = settingService.patchSignOut() +} diff --git a/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt b/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt index bd982038..7a5f50c2 100644 --- a/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt +++ b/data/src/main/java/com/going/data/dto/request/SignInRequestDto.kt @@ -1,14 +1,13 @@ package com.going.data.dto.request import com.going.domain.entity.request.RequestSignInModel -import com.going.domain.entity.request.RequestSignUpModel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class SignInRequestDto( @SerialName("platform") - val platform: String + val platform: String, ) fun RequestSignInModel.toSignInRequestDto(): SignInRequestDto = diff --git a/data/src/main/java/com/going/data/dto/response/SignOutResponseDto.kt b/data/src/main/java/com/going/data/dto/response/SignOutResponseDto.kt new file mode 100644 index 00000000..6a03f5e2 --- /dev/null +++ b/data/src/main/java/com/going/data/dto/response/SignOutResponseDto.kt @@ -0,0 +1,16 @@ +package com.going.data.dto.response + +import com.going.domain.entity.response.SignOutModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class SignOutResponseDto( + @SerialName("status") + val status: Int, + @SerialName("message") + val message: String, +) { + fun toSignOutModel() = + SignOutModel(status, message) +} diff --git a/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt new file mode 100644 index 00000000..e1045509 --- /dev/null +++ b/data/src/main/java/com/going/data/repositoryImpl/SettingRepositoryImpl.kt @@ -0,0 +1,14 @@ +package com.going.data.repositoryImpl + +import com.going.data.datasource.SettingDataSource +import com.going.domain.entity.response.SignOutModel +import com.going.domain.repository.SettingRepository +import javax.inject.Inject + +class SettingRepositoryImpl @Inject constructor( + private val settingDataSource: SettingDataSource, +) : SettingRepository { + override suspend fun patchSignOut(): Result = runCatching { + settingDataSource.patchSignOut().toSignOutModel() + } +} diff --git a/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt b/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt index ba4b219d..ab3efccb 100644 --- a/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt +++ b/data/src/main/java/com/going/data/repositoryImpl/TokenRepositoryImpl.kt @@ -8,11 +8,15 @@ class TokenRepositoryImpl @Inject constructor( private val goingDataStore: GoingDataStore, ) : TokenRepository { override fun getAccessToken(): String = goingDataStore.accessToken + override fun getRefreshToken(): String = goingDataStore.refreshToken override fun setTokens(accessToken: String, refreshToken: String) { goingDataStore.accessToken = accessToken goingDataStore.refreshToken = refreshToken } - override fun getRefreshToken(): String = goingDataStore.refreshToken + override fun clearTokens() { + goingDataStore.accessToken = "" + goingDataStore.refreshToken = "" + } } diff --git a/data/src/main/java/com/going/data/service/SettingService.kt b/data/src/main/java/com/going/data/service/SettingService.kt new file mode 100644 index 00000000..6473cacd --- /dev/null +++ b/data/src/main/java/com/going/data/service/SettingService.kt @@ -0,0 +1,9 @@ +package com.going.data.service + +import com.going.data.dto.response.SignOutResponseDto +import retrofit2.http.PATCH + +interface SettingService { + @PATCH("api/users/signout") + suspend fun patchSignOut(): SignOutResponseDto +} diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/SignOutModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/SignOutModel.kt new file mode 100644 index 00000000..991849e2 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/entity/response/SignOutModel.kt @@ -0,0 +1,6 @@ +package com.going.domain.entity.response + +data class SignOutModel( + val status: Int, + val message: String, +) diff --git a/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt new file mode 100644 index 00000000..456a23d0 --- /dev/null +++ b/domain/src/main/kotlin/com/going/domain/repository/SettingRepository.kt @@ -0,0 +1,7 @@ +package com.going.domain.repository + +import com.going.domain.entity.response.SignOutModel + +interface SettingRepository { + suspend fun patchSignOut(): Result +} diff --git a/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt b/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt index dbcbbff1..45d8a7be 100644 --- a/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt +++ b/domain/src/main/kotlin/com/going/domain/repository/TokenRepository.kt @@ -6,4 +6,5 @@ interface TokenRepository { fun getRefreshToken(): String fun setTokens(accessToken: String, refreshToken: String) + fun clearTokens() } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt index 5ea3c164..fae8ed79 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashViewModel.kt @@ -9,5 +9,7 @@ import javax.inject.Inject class SplashViewModel @Inject constructor( private val tokenRepository: TokenRepository, ) : ViewModel() { - fun getHasAccessToken(): Boolean = tokenRepository.getAccessToken() != "" + fun getHasAccessToken(): Boolean = tokenRepository.getAccessToken().isNotBlank() + + fun clearTokens() = tokenRepository.clearTokens() } diff --git a/presentation/src/main/java/com/going/presentation/tendencytest/result/TendencyTestResultActivity.kt b/presentation/src/main/java/com/going/presentation/tendencytest/result/TendencyTestResultActivity.kt index e9d919e4..0ba5cb11 100644 --- a/presentation/src/main/java/com/going/presentation/tendencytest/result/TendencyTestResultActivity.kt +++ b/presentation/src/main/java/com/going/presentation/tendencytest/result/TendencyTestResultActivity.kt @@ -1,5 +1,6 @@ package com.going.presentation.tendencytest.result +import android.media.SubtitleData import android.os.Bundle import android.text.SpannableString import android.text.Spanned @@ -26,7 +27,7 @@ class TendencyTestResultActivity : with(binding) { tvTendencyTestResultTitle.text = getString(R.string.tendency_test_result_title, "찐두릅") - viewModel?.mockTendencyResult?.apply { + viewModel.mockTendencyResult.apply { tvTendencyTestResultType.text = profileTitle tvTendencyTestResultSubType.text = profileSubTitle diff --git a/presentation/src/main/java/com/going/presentation/util/JsonExt.kt b/presentation/src/main/java/com/going/presentation/util/JsonExt.kt index 9aea58f3..65e7d6b6 100644 --- a/presentation/src/main/java/com/going/presentation/util/JsonExt.kt +++ b/presentation/src/main/java/com/going/presentation/util/JsonExt.kt @@ -1,12 +1,17 @@ package com.going.presentation.util +import android.util.Log import org.json.JSONObject import retrofit2.HttpException fun toErrorCode(throwable: Throwable): String = if (throwable is HttpException) { val jsonTemp = throwable.response()?.errorBody()?.byteString().toString() - val json = jsonTemp.slice(6 until jsonTemp.length) + Log.e("TAG", "toErrorCode: $jsonTemp", ) + val json = jsonTemp.slice(6 until jsonTemp.length - 2) + "}" + Log.e("TAG", "toErrorCode: $json", ) JSONObject(json).getString("code") } else { "NOT_HTTP" } + +// 기능 오류 발견!!!! 반드시 수정 필요!!!!!! diff --git a/presentation/src/main/res/layout/activity_tendency_test_result.xml b/presentation/src/main/res/layout/activity_tendency_test_result.xml index b65bdda2..4d6d9e6b 100644 --- a/presentation/src/main/res/layout/activity_tendency_test_result.xml +++ b/presentation/src/main/res/layout/activity_tendency_test_result.xml @@ -3,9 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - +