From d54ec1506f50600c95146d3c7b3d9eace8829b54 Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 10 Aug 2024 11:01:36 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20login=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/network/api/auth/AuthService.kt | 13 ++++ .../core/network/di/service/AuthModule.kt | 17 +++++ .../model/request/sign/LoginRequest.kt | 6 ++ .../model/response/auth/TokenResponse.kt | 6 ++ .../model/response/auth/UserResponse.kt | 8 +++ .../di/repository/AuthRepositoryModule.kt | 18 ++++++ .../auth/di/strategy/LoginStrategyModule.kt | 20 ++++++ .../auth/repository/login/LoginRepository.kt | 7 ++ .../repository/login/LoginRepositoryIml.kt | 23 +++++++ .../data/auth/strategy/BaseStrategy.kt | 10 +++ .../data/auth/strategy/GoogleStrategy.kt | 64 +++++++++++++++++++ .../easyhz/noffice/data/auth/util/Provider.kt | 5 ++ .../domain/sign/usecase/LoginUseCase.kt | 13 ++++ .../sign/contract/login/LoginIntent.kt | 3 +- .../feature/sign/screen/login/LoginScreen.kt | 4 +- .../sign/screen/login/LoginViewModel.kt | 16 +++-- 16 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt create mode 100644 core/network/src/main/java/com/easyhz/noffice/core/network/di/service/AuthModule.kt create mode 100644 core/network/src/main/java/com/easyhz/noffice/core/network/model/request/sign/LoginRequest.kt create mode 100644 core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/TokenResponse.kt create mode 100644 core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/UserResponse.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/di/repository/AuthRepositoryModule.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/di/strategy/LoginStrategyModule.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepository.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepositoryIml.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/BaseStrategy.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/GoogleStrategy.kt create mode 100644 data/auth/src/main/java/com/easyhz/noffice/data/auth/util/Provider.kt create mode 100644 domain/sign/src/main/java/com/easyhz/noffice/domain/sign/usecase/LoginUseCase.kt diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt new file mode 100644 index 00000000..fb7ae001 --- /dev/null +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/api/auth/AuthService.kt @@ -0,0 +1,13 @@ +package com.easyhz.noffice.core.network.api.auth + +import com.easyhz.noffice.core.network.model.request.sign.LoginRequest +import com.easyhz.noffice.core.network.model.response.auth.UserResponse +import retrofit2.http.Body +import retrofit2.http.POST + +interface AuthService { + @POST("/api/v1/member/login") + suspend fun login( + @Body body: LoginRequest + ): Result +} \ No newline at end of file diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/di/service/AuthModule.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/di/service/AuthModule.kt new file mode 100644 index 00000000..48f5a9f3 --- /dev/null +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/di/service/AuthModule.kt @@ -0,0 +1,17 @@ +package com.easyhz.noffice.core.network.di.service + +import com.easyhz.noffice.core.network.api.auth.AuthService +import com.easyhz.noffice.core.network.di.NofficeRetrofit +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import retrofit2.Retrofit + +@Module +@InstallIn(SingletonComponent::class) +object AuthModule { + @Provides + fun provideAuthService(@NofficeRetrofit retrofit: Retrofit): AuthService = + retrofit.create(AuthService::class.java) +} \ No newline at end of file diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/sign/LoginRequest.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/sign/LoginRequest.kt new file mode 100644 index 00000000..0e1c593d --- /dev/null +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/model/request/sign/LoginRequest.kt @@ -0,0 +1,6 @@ +package com.easyhz.noffice.core.network.model.request.sign + +data class LoginRequest( + val code: String, + val provider: String +) diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/TokenResponse.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/TokenResponse.kt new file mode 100644 index 00000000..0c47af9b --- /dev/null +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/TokenResponse.kt @@ -0,0 +1,6 @@ +package com.easyhz.noffice.core.network.model.response.auth + +data class TokenResponse( + val accessToken: String, + val refreshToken: String +) diff --git a/core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/UserResponse.kt b/core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/UserResponse.kt new file mode 100644 index 00000000..d49b4254 --- /dev/null +++ b/core/network/src/main/java/com/easyhz/noffice/core/network/model/response/auth/UserResponse.kt @@ -0,0 +1,8 @@ +package com.easyhz.noffice.core.network.model.response.auth + +data class UserResponse( + val memberId: String, + val memberName: String, + val provider: String, + val token: TokenResponse +) \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/di/repository/AuthRepositoryModule.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/di/repository/AuthRepositoryModule.kt new file mode 100644 index 00000000..26799668 --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/di/repository/AuthRepositoryModule.kt @@ -0,0 +1,18 @@ +package com.easyhz.noffice.data.auth.di.repository + +import com.easyhz.noffice.data.auth.repository.login.LoginRepository +import com.easyhz.noffice.data.auth.repository.login.LoginRepositoryIml +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +interface AuthRepositoryModule { + + @Binds + fun bindAuthRepository( + loginRepositoryIml: LoginRepositoryIml + ): LoginRepository +} \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/di/strategy/LoginStrategyModule.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/di/strategy/LoginStrategyModule.kt new file mode 100644 index 00000000..5f67d2c6 --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/di/strategy/LoginStrategyModule.kt @@ -0,0 +1,20 @@ +package com.easyhz.noffice.data.auth.di.strategy + +import com.easyhz.noffice.data.auth.strategy.BaseStrategy +import com.easyhz.noffice.data.auth.strategy.GoogleStrategy +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class LoginStrategyModule { + + @Binds + @Singleton + abstract fun bindGoogleStrategy( + googleStrategy: GoogleStrategy + ): BaseStrategy +} \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepository.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepository.kt new file mode 100644 index 00000000..7848271f --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepository.kt @@ -0,0 +1,7 @@ +package com.easyhz.noffice.data.auth.repository.login + +import android.content.Context + +interface LoginRepository { + suspend fun loginWithGoogle(context: Context): Result +} \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepositoryIml.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepositoryIml.kt new file mode 100644 index 00000000..e127f56f --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/repository/login/LoginRepositoryIml.kt @@ -0,0 +1,23 @@ +package com.easyhz.noffice.data.auth.repository.login + +import android.content.Context +import com.easyhz.noffice.core.network.api.auth.AuthService +import com.easyhz.noffice.core.network.model.request.sign.LoginRequest +import com.easyhz.noffice.data.auth.strategy.GoogleStrategy +import com.easyhz.noffice.data.auth.util.Provider +import javax.inject.Inject + +class LoginRepositoryIml @Inject constructor( + private val googleStrategy: GoogleStrategy, + private val authService: AuthService +): LoginRepository { + override suspend fun loginWithGoogle(context: Context): Result = runCatching { + val authorizationCode = googleStrategy.login(context).getOrThrow() + val request = LoginRequest( + code = authorizationCode, + provider = Provider.GOOGLE.name + ) + + val a = authService.login(request).getOrThrow() + } +} \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/BaseStrategy.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/BaseStrategy.kt new file mode 100644 index 00000000..9b8b961a --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/BaseStrategy.kt @@ -0,0 +1,10 @@ +package com.easyhz.noffice.data.auth.strategy + +import android.content.Context + +interface BaseStrategy { + + suspend fun login(context: Context): Result + + suspend fun logout() +} \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/GoogleStrategy.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/GoogleStrategy.kt new file mode 100644 index 00000000..70d2f2cf --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/strategy/GoogleStrategy.kt @@ -0,0 +1,64 @@ +package com.easyhz.noffice.data.auth.strategy + +import android.content.Context +import android.util.Log +import androidx.credentials.CredentialManager +import androidx.credentials.CustomCredential +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetCredentialResponse +import com.easyhz.noffice.core.common.error.NofficeError +import com.easyhz.noffice.data.auth.BuildConfig +import com.google.android.libraries.identity.googleid.GetGoogleIdOption +import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential +import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException +import javax.inject.Inject + +class GoogleStrategy @Inject constructor( +): BaseStrategy { + private val tag = this.javaClass.name + + override suspend fun login(context: Context): Result = runCatching { + val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder() + .setServerClientId(BuildConfig.GOOGLE_CLIENT_ID) + .setFilterByAuthorizedAccounts(false) + .setAutoSelectEnabled(false) + .build() + + val request: GetCredentialRequest = + GetCredentialRequest.Builder().addCredentialOption( + googleIdOption + ).build() + + val result = + CredentialManager.create(context) + .getCredential(context = context, request = request) + handleGoogleSignIn(result) + } + + override suspend fun logout() { + TODO("Not yet implemented") + } + + private fun handleGoogleSignIn(result: GetCredentialResponse): String { + when (val credential = result.credential) { + is CustomCredential -> { + if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { + try { + val googleIdTokenCredential = GoogleIdTokenCredential + .createFrom(credential.data) + return googleIdTokenCredential.idToken + } catch (e: GoogleIdTokenParsingException) { + throw e + } + } else { + Log.d(tag, "Unexpected type of credential (not TYPE_GOOGLE_ID_TOKEN_CREDENTIAL). ") + throw NofficeError.UnexpectedError + } + } + else -> { + Log.d(tag, "Unexpected type of credential (not custom).") + throw NofficeError.UnexpectedError + } + } + } +} \ No newline at end of file diff --git a/data/auth/src/main/java/com/easyhz/noffice/data/auth/util/Provider.kt b/data/auth/src/main/java/com/easyhz/noffice/data/auth/util/Provider.kt new file mode 100644 index 00000000..7d686043 --- /dev/null +++ b/data/auth/src/main/java/com/easyhz/noffice/data/auth/util/Provider.kt @@ -0,0 +1,5 @@ +package com.easyhz.noffice.data.auth.util + +enum class Provider { + GOOGLE +} \ No newline at end of file diff --git a/domain/sign/src/main/java/com/easyhz/noffice/domain/sign/usecase/LoginUseCase.kt b/domain/sign/src/main/java/com/easyhz/noffice/domain/sign/usecase/LoginUseCase.kt new file mode 100644 index 00000000..9654ce84 --- /dev/null +++ b/domain/sign/src/main/java/com/easyhz/noffice/domain/sign/usecase/LoginUseCase.kt @@ -0,0 +1,13 @@ +package com.easyhz.noffice.domain.sign.usecase + +import android.content.Context +import com.easyhz.noffice.core.common.base.BaseUseCase +import com.easyhz.noffice.data.auth.repository.login.LoginRepository +import javax.inject.Inject + +class LoginUseCase @Inject constructor( + private val loginRepository: LoginRepository +): BaseUseCase() { + override suspend fun invoke(param: Context): Result = + loginRepository.loginWithGoogle(param) +} \ No newline at end of file diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/login/LoginIntent.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/login/LoginIntent.kt index 5afe9ea3..d91a39f8 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/login/LoginIntent.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/login/LoginIntent.kt @@ -1,7 +1,8 @@ package com.easyhz.noffice.feature.sign.contract.login +import android.content.Context import com.easyhz.noffice.core.common.base.UiIntent sealed class LoginIntent : UiIntent() { - data object ClickToLogInWithGoogle: LoginIntent() + data class ClickToLogInWithGoogle(val context: Context): LoginIntent() } \ No newline at end of file diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginScreen.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginScreen.kt index 863b0ac5..5a61e72a 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginScreen.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -29,6 +30,7 @@ fun LoginScreen( viewModel: LoginViewModel = hiltViewModel(), navigateToHome: () -> Unit ) { + val context = LocalContext.current NofficeBasicScaffold { Box(modifier = modifier.fillMaxSize()) { Image( @@ -59,7 +61,7 @@ fun LoginScreen( .weight(0.2f), onClickSocial = object :SocialLoginType.OnItemClickListener { override fun onClickGoogle() { - viewModel.postIntent(LoginIntent.ClickToLogInWithGoogle) + viewModel.postIntent(LoginIntent.ClickToLogInWithGoogle(context)) } } ) diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginViewModel.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginViewModel.kt index 4b9dd54a..c310a608 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginViewModel.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/login/LoginViewModel.kt @@ -3,28 +3,36 @@ package com.easyhz.noffice.feature.sign.screen.login import android.content.Context import androidx.lifecycle.viewModelScope import com.easyhz.noffice.core.common.base.BaseViewModel +import com.easyhz.noffice.domain.sign.usecase.LoginUseCase import com.easyhz.noffice.feature.sign.contract.login.LoginIntent import com.easyhz.noffice.feature.sign.contract.login.LoginSideEffect import com.easyhz.noffice.feature.sign.contract.login.LoginState import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( - @ApplicationContext private val context: Context + private val loginUseCase: LoginUseCase ): BaseViewModel( initialState = LoginState.init() ) { override fun handleIntent(intent: LoginIntent) { when(intent) { - is LoginIntent.ClickToLogInWithGoogle -> { onClickToLogInWithGoogle() } + is LoginIntent.ClickToLogInWithGoogle -> { onClickToLogInWithGoogle(intent.context) } } } - private fun onClickToLogInWithGoogle() = viewModelScope.launch { + private fun onClickToLogInWithGoogle(context: Context) = viewModelScope.launch { // TODO 로그인 로직 처리 + loginUseCase.invoke(context).onSuccess { + println("success") + }.onFailure { + it.printStackTrace() + } + } + + private fun navigateToHome() { postSideEffect { LoginSideEffect.NavigateToHome } } } \ No newline at end of file