Skip to content

Commit

Permalink
feat: 최초로그인 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
rhkrwngud445 committed Apr 2, 2024
1 parent cd49eb3 commit 56d674f
Show file tree
Hide file tree
Showing 20 changed files with 228 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ fun WithpeaceNavHost(
onNavigateToGallery = {
navController.navigateToGallery(imageLimit = 1)
},
onSignUpSuccess = {
navController.navigateHome(
navOptions = navOptions {
popUpTo(navController.graph.id) {
inclusive = true
}
},
)
},
)
registerPostNavGraph(
onShowSnackBar = onShowSnackBar,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.withpeace.withpeace.core.data.repository

import android.content.Context
import com.skydoves.sandwich.message
import com.skydoves.sandwich.suspendMapSuccess
import com.skydoves.sandwich.suspendOnFailure
import com.withpeace.withpeace.core.data.mapper.roleToDomain
import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource
import com.withpeace.withpeace.core.domain.model.role.Role
import com.withpeace.withpeace.core.domain.repository.TokenRepository
import com.withpeace.withpeace.core.network.di.request.SignUpRequest
import com.withpeace.withpeace.core.network.di.service.AuthService
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
Expand All @@ -17,6 +18,7 @@ import kotlinx.coroutines.flow.flowOn
import javax.inject.Inject

class DefaultTokenRepository @Inject constructor(
@ApplicationContext private val context: Context,
private val tokenPreferenceDataSource: TokenPreferenceDataSource,
private val authService: AuthService,
) : TokenRepository {
Expand All @@ -25,31 +27,14 @@ class DefaultTokenRepository @Inject constructor(
return token != null
}

override suspend fun signUp(
email: String,
nickname: String,
onError: (String) -> Unit,
): Flow<Unit> = flow {
authService.signUp(
SignUpRequest(email = email, nickname = nickname, deviceToken = null),
).suspendMapSuccess {
val data = this.data
tokenPreferenceDataSource.updateAccessToken(data.accessToken)
tokenPreferenceDataSource.updateRefreshToken(data.refreshToken)
emit(Unit)
}.suspendOnFailure { onError(message()) }
}.flowOn(Dispatchers.IO)

override fun getTokenByGoogle(
idToken: String,
onError: (String) -> Unit,
): Flow<Role> = flow {
authService.googleLogin(AUTHORIZATION_FORMAT.format(idToken)).suspendMapSuccess {
val data = this.data
if (data.role.roleToDomain() == Role.USER) {
tokenPreferenceDataSource.updateAccessToken(data.tokenResponse.accessToken)
tokenPreferenceDataSource.updateRefreshToken(data.tokenResponse.refreshToken)
}
tokenPreferenceDataSource.updateAccessToken(data.tokenResponse.accessToken)
tokenPreferenceDataSource.updateRefreshToken(data.tokenResponse.refreshToken)
emit(data.role.roleToDomain())
}.suspendOnFailure { onError(message()) }
}.flowOn(Dispatchers.IO)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.skydoves.sandwich.suspendOnException
import com.withpeace.withpeace.core.data.mapper.toDomain
import com.withpeace.withpeace.core.data.util.convertToFile
import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource
import com.withpeace.withpeace.core.domain.model.SignUpInfo
import com.withpeace.withpeace.core.domain.model.WithPeaceError
import com.withpeace.withpeace.core.domain.model.profile.ChangedProfile
import com.withpeace.withpeace.core.domain.model.profile.Nickname
Expand Down Expand Up @@ -49,12 +50,37 @@ class DefaultUserRepository @Inject constructor(
}
}

override fun registerProfile(
nickname: String,
profileImage: String,
onError: (WithPeaceError) -> Unit,
): Flow<Unit> {
TODO("Not yet implemented")
override suspend fun signUp(
signUpInfo: SignUpInfo,
onError: suspend (WithPeaceError) -> Unit,
): Flow<Unit> = flow {
val nicknameRequestBody =
signUpInfo.nickname.toRequestBody("text/plain".toMediaTypeOrNull())
val request =
if (signUpInfo.profileImage.isNullOrEmpty()) {
userService.signUp(
nicknameRequestBody,
)
} else {
val profileImagePart = getImagePart(signUpInfo.profileImage!!)
userService.signUp(nicknameRequestBody, profileImagePart)
}

request.suspendMapSuccess {
val data = this.data
tokenPreferenceDataSource.updateAccessToken(data.accessToken)
tokenPreferenceDataSource.updateRefreshToken(data.refreshToken)
emit(Unit)
}.suspendOnError {
if (statusCode.code == 401) {
onError(WithPeaceError.UnAuthorized())
} else {
val errorBody = errorBody?.getErrorBody()
onError(WithPeaceError.GeneralError(errorBody?.code, errorBody?.message))
}
}.suspendOnException {
onError(WithPeaceError.GeneralError(message = messageOrNull))
}
}

override fun updateProfile(
Expand Down Expand Up @@ -134,6 +160,8 @@ class DefaultUserRepository @Inject constructor(
}
}



override fun logout(onError: suspend (WithPeaceError) -> Unit): Flow<Unit> = flow {
userService.logout().suspendMapSuccess {
tokenPreferenceDataSource.removeAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.withpeace.withpeace.core.domain.model

data class SignUpInfo(
val nickname: String,
val profileImage: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,8 @@ import com.withpeace.withpeace.core.domain.model.role.Role
import kotlinx.coroutines.flow.Flow

interface TokenRepository {

suspend fun isLogin(): Boolean

suspend fun signUp(
email: String,
nickname: String,
onError: (String) -> Unit,
): Flow<Unit>

fun getTokenByGoogle(
idToken: String,
onError: (String) -> Unit,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.withpeace.withpeace.core.domain.repository

import com.withpeace.withpeace.core.domain.model.SignUpInfo
import com.withpeace.withpeace.core.domain.model.WithPeaceError
import com.withpeace.withpeace.core.domain.model.profile.ChangedProfile
import com.withpeace.withpeace.core.domain.model.profile.Nickname
Expand All @@ -8,10 +9,6 @@ import kotlinx.coroutines.flow.Flow

interface UserRepository {
fun getProfile(onError: suspend (WithPeaceError) -> Unit): Flow<ProfileInfo>
fun registerProfile(
nickname: String, profileImage: String,
onError: (WithPeaceError) -> Unit,
): Flow<Unit>

fun updateProfileImage(
profileImage: String,
Expand All @@ -34,4 +31,8 @@ interface UserRepository {
): Flow<Unit>

fun logout(onError: suspend (WithPeaceError) -> Unit): Flow<Unit>
suspend fun signUp(
signUpInfo: SignUpInfo,
onError: suspend (WithPeaceError) -> Unit
): Flow<Unit>
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package com.withpeace.withpeace.core.domain.usecase

import com.withpeace.withpeace.core.domain.model.SignUpInfo
import com.withpeace.withpeace.core.domain.model.WithPeaceError
import com.withpeace.withpeace.core.domain.repository.TokenRepository
import com.withpeace.withpeace.core.domain.repository.UserRepository
import javax.inject.Inject

class SignUpUseCase @Inject constructor(
private val tokenRepository: TokenRepository,
private val userRepository: UserRepository,
) {
suspend operator fun invoke(
email: String,
nickname: String,
onError: (String) -> Unit,
) = tokenRepository.signUp(
email = email,
nickname = nickname,
signUpInfo: SignUpInfo,
onError: suspend (WithPeaceError) -> Unit,
) = userRepository.signUp(
signUpInfo,
onError = onError,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.withpeace.withpeace.core.domain.usecase

import com.withpeace.withpeace.core.domain.model.SignUpInfo
import com.withpeace.withpeace.core.domain.model.WithPeaceError
import com.withpeace.withpeace.core.domain.repository.TokenRepository
import com.withpeace.withpeace.core.domain.repository.UserRepository
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
Expand All @@ -11,43 +14,53 @@ import org.junit.Test

class SignUpUseCaseTest {
private lateinit var signUpUseCase: SignUpUseCase
private val tokenRepository: TokenRepository = mockk(relaxed = true)
private val userRepository: UserRepository = mockk(relaxed = true)

private fun initialize() = SignUpUseCase(tokenRepository)
private fun initialize() = SignUpUseCase(userRepository)

@Test
fun `회원가입에 성공하면, 성공응답을 반환한다`() = runTest {
// given
val onSuccess = mockk<() -> Unit>(relaxed = true)
coEvery {
tokenRepository.signUp(
"Email",
"nickname",
userRepository.signUp(
SignUpInfo(
"Email",
"nickname",
),
onError = any(),
)
} returns flow { onSuccess.invoke() }
signUpUseCase = initialize()
// when
signUpUseCase("Email", "nickname", {}).collect()
signUpUseCase(SignUpInfo("Email", "nickname"), {}).collect()
// then
coVerify { onSuccess.invoke() }
}

@Test
fun `회원가입에 실패하면, 메세지가 담긴 실패응답을 반환한다`() = runTest {
// given
val errorMock = mockk<(String) -> Unit>(relaxed = true)
val errorMock = mockk<(WithPeaceError) -> Unit>(relaxed = true)
coEvery {
tokenRepository.signUp(
"Email",
"nickname",
userRepository.signUp(
SignUpInfo(
"Email",
"nickname",
),
onError = errorMock,
)
} returns flow { errorMock("test") }
} returns flow { errorMock(WithPeaceError.UnAuthorized()) }
signUpUseCase = initialize()
// when
signUpUseCase("Email", "nickname", onError = errorMock).collect()
signUpUseCase(
SignUpInfo(
"Email",
"nickname",
),
onError = errorMock,
).collect()
// then
coVerify { errorMock("test") }
coVerify { errorMock(WithPeaceError.UnAuthorized()) }
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
package com.withpeace.withpeace.core.network.di.request

import kotlinx.serialization.Serializable

@Serializable
data class SignUpRequest(
val email: String,
val nickname: String,
val deviceToken: String?,
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.withpeace.withpeace.core.network.di.service

import com.skydoves.sandwich.ApiResponse
import com.withpeace.withpeace.core.network.di.request.SignUpRequest
import com.withpeace.withpeace.core.network.di.response.BaseResponse
import com.withpeace.withpeace.core.network.di.response.LoginResponse
import com.withpeace.withpeace.core.network.di.response.TokenResponse
import retrofit2.http.Body
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part

interface AuthService {

Expand All @@ -22,9 +24,4 @@ interface AuthService {

@POST("/api/v1/auth/logout")
suspend fun logout(): ApiResponse<BaseResponse<String>>

@POST("/api/v1/auth/register")
suspend fun signUp(
@Body signUpRequest: SignUpRequest,
): ApiResponse<BaseResponse<TokenResponse>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.withpeace.withpeace.core.network.di.request.NicknameRequest
import com.withpeace.withpeace.core.network.di.response.BaseResponse
import com.withpeace.withpeace.core.network.di.response.ChangedProfileResponse
import com.withpeace.withpeace.core.network.di.response.ProfileResponse
import com.withpeace.withpeace.core.network.di.response.TokenResponse
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.http.Body
Expand Down Expand Up @@ -39,4 +40,17 @@ interface UserService {

@POST("/api/v1/auth/logout")
suspend fun logout(): ApiResponse<BaseResponse<String>>

@Multipart
@POST("/api/v1/auth/register")
suspend fun signUp(
@Part("nickname") nickname: RequestBody,
@Part imageFile: MultipartBody.Part,
): ApiResponse<BaseResponse<TokenResponse>>

@Multipart
@POST("/api/v1/auth/register")
suspend fun signUp(
@Part("nickname") nickname: RequestBody,
): ApiResponse<BaseResponse<TokenResponse>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.withpeace.withpeace.core.domain.model.role.Role
import com.withpeace.withpeace.core.domain.usecase.GoogleLoginUseCase
import com.withpeace.withpeace.core.domain.usecase.SignUpUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
Expand All @@ -14,7 +13,6 @@ import javax.inject.Inject
@HiltViewModel
class LoginViewModel @Inject constructor(
private val googleLoginUseCase: GoogleLoginUseCase,
private val signUpUseCase: SignUpUseCase,
) : ViewModel() {

private val _loginUiEvent: Channel<LoginUiEvent> = Channel()
Expand All @@ -37,17 +35,4 @@ class LoginViewModel @Inject constructor(
}
}
}

fun signUp(
email: String,
nickname: String,
) {
viewModelScope.launch {
signUpUseCase(
email = email,
nickname = nickname,
onError = { launch { _loginUiEvent.send(LoginUiEvent.SignUpFail(it)) } },
).collect { _loginUiEvent.send(LoginUiEvent.SignUpSuccess) }
}
}
}
Loading

0 comments on commit 56d674f

Please sign in to comment.