Skip to content

Commit

Permalink
Merge pull request #72 from team-JMT/feat/jmtException_sandbox
Browse files Browse the repository at this point in the history
JmtException 실험용 코드(커스텀 Exeption)
  • Loading branch information
dogdduddy authored Sep 16, 2023
2 parents 9c12143 + 5c54ae2 commit e19fa23
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package org.gdsc.data.datasource

import org.gdsc.domain.model.Response
import org.gdsc.domain.model.response.TokenResponse

interface LoginDataSource {
suspend fun postSignUpWithGoogleToken(token: String): TokenResponse
suspend fun postSignUpWithGoogleToken(token: String): Result<Response<TokenResponse>>

suspend fun postAppleToken(email: String, clientId: String): TokenResponse

Expand Down
16 changes: 14 additions & 2 deletions data/src/main/java/org/gdsc/data/datasource/LoginDataSourceImpl.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.gdsc.data.datasource

import org.gdsc.domain.model.Response
import org.gdsc.data.network.LoginAPI
import org.gdsc.domain.exception.JmtException
import org.gdsc.domain.model.request.AppleLoginRequest
import org.gdsc.domain.model.request.GoogleLoginRequest
import org.gdsc.domain.model.response.TokenResponse
Expand All @@ -10,8 +12,18 @@ import javax.inject.Inject
class LoginDataSourceImpl @Inject constructor(
private val loginAPI: LoginAPI
) : LoginDataSource {
override suspend fun postSignUpWithGoogleToken(token: String): TokenResponse {
return loginAPI.postUserGoogleToken(GoogleLoginRequest(token)).data
override suspend fun postSignUpWithGoogleToken(token: String): Result<Response<TokenResponse>> {
return runCatching {
loginAPI.postUserGoogleToken(GoogleLoginRequest(token)).body()
?: throw JmtException.NoneDataException()
}.onFailure {
throw if (it.message != null) {
JmtException.GeneralException(requireNotNull(it.message))
} else {
JmtException.UnKnownException()
}
}

}

override suspend fun postAppleToken(email: String, clientId: String): TokenResponse {
Expand Down
6 changes: 2 additions & 4 deletions data/src/main/java/org/gdsc/data/network/LoginAPI.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package org.gdsc.data.network


import org.gdsc.data.model.Response
import org.gdsc.domain.model.Response
import org.gdsc.domain.model.request.GoogleLoginRequest
import org.gdsc.domain.model.request.AppleLoginRequest
import org.gdsc.domain.model.response.TokenResponse
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.HTTP
import retrofit2.http.POST

interface LoginAPI {
@POST("api/v1/auth/google")
suspend fun postUserGoogleToken(
@Body request: GoogleLoginRequest
): Response<TokenResponse>
): retrofit2.Response<Response<TokenResponse>>

@POST("api/v1/auth/android/apple")
suspend fun postUserAppleToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import javax.inject.Inject
class LoginRepositoryImpl @Inject constructor(private val loginDataSource: LoginDataSource) :
LoginRepository {

override suspend fun postSignUpWithGoogleToken(token: String): TokenResponse {
return loginDataSource.postSignUpWithGoogleToken(token)
override suspend fun postSignUpWithGoogleToken(token: String): Result<TokenResponse> {
return kotlin.runCatching {
loginDataSource.postSignUpWithGoogleToken(token).getOrThrow().data
}
}

override suspend fun postAppleToken(email: String, clientId: String): TokenResponse {
Expand Down
15 changes: 15 additions & 0 deletions domain/src/main/java/org/gdsc/domain/exception/JmtException.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.gdsc.domain.exception

import org.gdsc.domain.Empty

sealed class JmtException(
message: String = String.Empty
) : Exception(message) {

data class NetworkException(override val message: String = "네트워크 연결을 확인해주세요.") : JmtException()
data class ServerException(override val message: String = "서버에 문제가 발생했습니다.") : JmtException()
data class NoneDataException(override val message: String = "데이터가 없습니다.") : JmtException()
data class UnKnownException(override val message: String = "알 수 없는 에러입니다.") : JmtException()
data class GeneralException(override val message: String) : JmtException()

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.gdsc.domain.model.response.TokenResponse

interface LoginRepository {

suspend fun postSignUpWithGoogleToken(token: String): TokenResponse
suspend fun postSignUpWithGoogleToken(token: String): Result<TokenResponse>

suspend fun postAppleToken(email: String, clientId: String): TokenResponse

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import javax.inject.Singleton
class PostSignUpWithGoogleTokenUseCase @Inject constructor(
private val loginRepository: LoginRepository
) {
suspend operator fun invoke(token: String): TokenResponse {
suspend operator fun invoke(token: String): Result<TokenResponse> {
return loginRepository.postSignUpWithGoogleToken(token)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import kotlinx.coroutines.launch
import org.gdsc.domain.model.response.UserLoginAction
import org.gdsc.presentation.R
import org.gdsc.presentation.databinding.FragmentLoginBinding
import org.gdsc.presentation.utils.repeatWhenUiStarted
import org.gdsc.presentation.view.LoginManager
import org.gdsc.presentation.view.MainActivity

Expand All @@ -45,6 +46,13 @@ class LoginFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setLoginButton()

repeatWhenUiStarted {
// TODO: specific handling
viewModel.eventFlow.collect {
Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT).show()
}
}
}

private fun setLoginButton() {
Expand All @@ -57,7 +65,7 @@ class LoginFragment : Fragment() {
.build()
)
} catch (e: ApiException) {
Log.e("Login","ApiException $e")
Log.e("Login", "ApiException $e")
showGoogleAccountRegistrationPrompt()
} catch (e: Exception) {
Log.e("Login", "setLoginButton Exception $e")
Expand Down Expand Up @@ -96,12 +104,13 @@ class LoginFragment : Fragment() {
val credential = loginManager.oneTapClient.getSignInCredentialFromIntent(intent)
credential.googleIdToken?.let {
viewModel.postSignUpWithGoogleToken(it) { tokenResponse ->
when(tokenResponse.userLoginAction) {
when (tokenResponse.userLoginAction) {
UserLoginAction.SIGN_UP.value -> {
val action =
LoginFragmentDirections.actionLoginFragmentToSignUpNicknameFragment()
findNavController().navigate(action)
}

UserLoginAction.LOG_IN.value -> {
val intent = Intent(requireContext(), MainActivity::class.java)
startActivity(intent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import okhttp3.MultipartBody
import org.gdsc.domain.Empty
import org.gdsc.domain.exception.JmtException
import org.gdsc.domain.model.response.TokenResponse
import org.gdsc.domain.usecase.CheckDuplicatedNicknameUseCase
import org.gdsc.domain.usecase.PostNicknameUseCase
import org.gdsc.domain.usecase.PostSignUpWithGoogleTokenUseCase
import org.gdsc.domain.usecase.token.SaveTokenUseCase
import org.gdsc.domain.usecase.user.PostDefaultProfileImageUseCase
import org.gdsc.domain.usecase.user.PostProfileImageUseCase
import org.gdsc.presentation.utils.MutableEventFlow
import org.gdsc.presentation.utils.asEventFlow
import javax.inject.Inject

@HiltViewModel
Expand All @@ -37,6 +40,9 @@ class LoginViewModel @Inject constructor(
private var _profileImageState = MutableStateFlow(String.Empty)
val profileImageState = _profileImageState.asStateFlow()

private var _eventFlow = MutableEventFlow<JmtException>()
val eventFlow = _eventFlow.asEventFlow()

val isNicknameVerified: StateFlow<Boolean>
get() = nicknameState.map { nickname ->
nickname.isNotBlank()
Expand All @@ -56,9 +62,13 @@ class LoginViewModel @Inject constructor(

fun postSignUpWithGoogleToken(token: String, afterSuccessSignUp: (TokenResponse) -> Unit) {
viewModelScope.launch {
val response = postSignUpWithGoogleTokenUseCase.invoke(token)
saveTokenUseCase.invoke(response)
afterSuccessSignUp(response)
val result = postSignUpWithGoogleTokenUseCase.invoke(token)
result.onSuccess { response ->
saveTokenUseCase.invoke(response)
afterSuccessSignUp(response)
}.onFailure {
_eventFlow.emit(it as JmtException)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.gdsc.domain.usecase.token.GetRefreshTokenUseCase
Expand Down Expand Up @@ -34,7 +33,7 @@ class SplashActivity : AppCompatActivity() {
setContentView(R.layout.activity_splash)
setToFullPage()

lifecycleScope.launch(Dispatchers.Main) {
lifecycleScope.launch {

val accessible = validateToken()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.gdsc.presentation.utils

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableSharedFlow
import org.gdsc.presentation.utils.EventFlow.Companion.DEFAULT_REPLAY
import java.util.concurrent.atomic.AtomicBoolean

interface EventFlow<out T> : Flow<T> {

companion object {
const val DEFAULT_REPLAY = 2
}
}

interface MutableEventFlow<T> : EventFlow<T>, FlowCollector<T>

private class EventFlowSlot<T>(val value: T) {

private val consumed = AtomicBoolean(false)

fun markConsumed() = consumed.getAndSet(true)
}

private class EventFlowImpl<T>(replay: Int) : MutableEventFlow<T> {

private val flow: MutableSharedFlow<EventFlowSlot<T>> = MutableSharedFlow(replay)

override suspend fun collect(collector: FlowCollector<T>) =
flow.collect { slot ->
if (slot.markConsumed().not()) {
collector.emit(slot.value)
}
}

override suspend fun emit(value: T) {
flow.emit(EventFlowSlot(value))
}
}

private class ReadOnlyEventFlow<T>(flow: EventFlow<T>) : EventFlow<T> by flow

fun <T> MutableEventFlow(replay: Int = DEFAULT_REPLAY): MutableEventFlow<T> = EventFlowImpl(replay)

fun <T> MutableEventFlow<T>.asEventFlow(): EventFlow<T> = ReadOnlyEventFlow(this)

0 comments on commit e19fa23

Please sign in to comment.