Skip to content

Commit

Permalink
🔀 Merge pull request #22 from snuhcs-course/test/authRepository-frontend
Browse files Browse the repository at this point in the history
Test/AuthRepository frontend
  • Loading branch information
sukchan-0811 authored Nov 5, 2023
2 parents e9447c7 + 3914cd9 commit 12118f2
Show file tree
Hide file tree
Showing 8 changed files with 508 additions and 72 deletions.
4 changes: 1 addition & 3 deletions frontend/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ dependencies {
implementation("androidx.compose.material3:material3")
implementation("androidx.test:monitor:1.6.1")
testImplementation("junit:junit:4.13.2")
testImplementation("org.json:json:20210307")
testImplementation("org.mockito:mockito-core:5.5.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
testImplementation("androidx.arch.core:core-testing:2.2.0")
Expand All @@ -73,8 +74,6 @@ dependencies {
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")

testImplementation("io.mockk:mockk:1.13.8")

// Lifecycle
val lifecycleVersion = "2.6.2"
implementation("androidx.lifecycle:lifecycle-common:$lifecycleVersion")
Expand All @@ -89,7 +88,6 @@ dependencies {
val coroutinesVersion = "1.7.1"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")

testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion")

// Preference
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.example.speechbuddy.data.remote.models

import android.util.Log
import com.example.speechbuddy.domain.models.ErrorResponse
import okhttp3.ResponseBody
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject

class ErrorResponseMapper {
fun mapToDomainModel(response: ResponseBody): ErrorResponse {
try {
val errorJson = JSONObject(response.charStream().readText()).optJSONObject("error")

if (errorJson != null) {
val code = errorJson.optInt("code", -1)
val messageJson = errorJson.opt("message")

// if "message" in the ResponseBody is a list
if (messageJson is JSONArray) {
val firstMessage = messageJson.optJSONObject(0)
if (firstMessage != null) {
val keys = firstMessage.keys()
if (keys.hasNext()) {
val key = keys.next()
val description = firstMessage.optJSONArray(key).optString(0)
return ErrorResponse(code, key, description)
}
}
}
// if "message" in the ResponseBody is a single json
else if (messageJson is JSONObject) {
val keys = messageJson.keys()
if (keys.hasNext()) {
val key = keys.next()
val description = messageJson.optString(key)
return ErrorResponse(code, key, description)
}
}
}

// Return a default ErrorResponse if the responseBody structure doesn't match predefined cases
return ErrorResponse()
} catch (e: JSONException) {
// Handle the JSON parsing error
Log.e("ErrorResponseMapper", "JSON parsing error: ${e.message}")
return ErrorResponse() // Return a default ErrorResponse in case of JSON parsing error
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.example.speechbuddy.di

import com.example.speechbuddy.MainApplication
import com.example.speechbuddy.data.remote.models.AuthTokenDtoMapper
import com.example.speechbuddy.data.remote.models.ErrorResponseMapper
import com.example.speechbuddy.service.AuthService
import com.example.speechbuddy.utils.Constants
import com.squareup.moshi.Moshi
Expand Down Expand Up @@ -49,6 +50,12 @@ class NetworkModule {
return AuthTokenDtoMapper()
}

@Singleton
@Provides
fun provideErrorResponseMapper(): ErrorResponseMapper {
return ErrorResponseMapper()
}

}

class AuthInterceptor: Interceptor {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.speechbuddy.domain.models

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class ErrorResponse(
val code: Int = -1,
val key: String = "Unknown Error With Error Response",
val description: String = "Unknown Error With Error Response"
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.example.speechbuddy.repository

import com.example.speechbuddy.data.remote.AuthTokenRemoteSource
import com.example.speechbuddy.data.remote.models.AuthTokenDtoMapper
import com.example.speechbuddy.data.remote.models.ErrorResponseMapper
import com.example.speechbuddy.data.remote.requests.AuthLoginRequest
import com.example.speechbuddy.data.remote.requests.AuthResetPasswordRequest
import com.example.speechbuddy.data.remote.requests.AuthSendCodeRequest
Expand All @@ -13,7 +14,6 @@ import com.example.speechbuddy.utils.Resource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import org.json.JSONObject
import retrofit2.Response
import javax.inject.Inject
import javax.inject.Singleton
Expand All @@ -22,74 +22,75 @@ import javax.inject.Singleton
class AuthRepository @Inject constructor(
private val authService: AuthService,
private val authTokenRemoteSource: AuthTokenRemoteSource,
private val authTokenDtoMapper: AuthTokenDtoMapper
private val authTokenDtoMapper: AuthTokenDtoMapper,
private val errorResponseMapper: ErrorResponseMapper,
) {

suspend fun signup(authSignupRequest: AuthSignupRequest): Flow<Response<Void>> =
flow {
val result = authService.signup(authSignupRequest)
emit(result)
}
suspend fun signup(authSignupRequest: AuthSignupRequest): Flow<Response<Void>> = flow {
val result = authService.signup(authSignupRequest)
emit(result)
}

suspend fun login(authLoginRequest: AuthLoginRequest): Flow<Resource<AuthToken>> {
return authTokenRemoteSource.loginAuthToken(authLoginRequest)
.map { response ->
return authTokenRemoteSource.loginAuthToken(authLoginRequest).map { response ->
if (response.isSuccessful && response.code() == 200) {
response.body()?.let { authTokenDto ->
authTokenDto.let {
Resource.success(authTokenDtoMapper.mapToDomainModel(authTokenDto))
Resource.success(
authTokenDtoMapper.mapToDomainModel(
authTokenDto
)
)
}
} ?: returnUnknownError()
} else {
response.errorBody()?.let { responseBody ->
val errorMessage =
JSONObject(responseBody.charStream().readText()).getString("error")
val errorMsgKey = errorResponseMapper.mapToDomainModel(responseBody).key
Resource.error(
errorMessage,
null
errorMsgKey, null
)
} ?: returnUnknownError()
}
}
}

suspend fun sendCodeForSignup(authVerifyEmailSendRequest: AuthSendCodeRequest): Flow<Response<Void>> =
suspend fun sendCodeForSignup(authSendCodeRequest: AuthSendCodeRequest): Flow<Response<Void>> =
flow {
val result = authService.sendCodeForSignup(authVerifyEmailSendRequest)
val result = authService.sendCodeForSignup(authSendCodeRequest)
emit(result)
}

suspend fun sendCodeForResetPassword(authVerifyEmailSendRequest: AuthSendCodeRequest): Flow<Response<Void>> =
suspend fun sendCodeForResetPassword(authSendCodeRequest: AuthSendCodeRequest): Flow<Response<Void>> =
flow {
val result = authService.sendCodeForResetPassword(authVerifyEmailSendRequest)
val result = authService.sendCodeForResetPassword(authSendCodeRequest)
emit(result)
}

suspend fun verifyEmailForSignup(authVerifyEmailAcceptRequest: AuthVerifyEmailRequest): Flow<Response<Void>> =
suspend fun verifyEmailForSignup(authVerifyEmailRequest: AuthVerifyEmailRequest): Flow<Response<Void>> =
flow {
val result = authService.verifyEmailForSignup(authVerifyEmailAcceptRequest)
val result = authService.verifyEmailForSignup(authVerifyEmailRequest)
emit(result)
}

suspend fun verifyEmailForResetPassword(authVerifyEmailAcceptRequest: AuthVerifyEmailRequest): Flow<Resource<AuthToken>> {
suspend fun verifyEmailForResetPassword(authVerifyEmailRequest: AuthVerifyEmailRequest): Flow<Resource<AuthToken>> {
return authTokenRemoteSource.verifyEmailForResetPasswordAuthToken(
authVerifyEmailAcceptRequest
)
.map { response ->
authVerifyEmailRequest
).map { response ->
if (response.isSuccessful && response.code() == 200) {
response.body()?.let { authTokenDto ->
authTokenDto.let {
Resource.success(authTokenDtoMapper.mapToDomainModel(authTokenDto))
Resource.success(
authTokenDtoMapper.mapToDomainModel(
authTokenDto
)
)
}
} ?: returnUnknownError()
} else {
response.errorBody()?.let { responseBody ->
val errorJson = JSONObject(responseBody.charStream().readText())
val messageJson = errorJson.getJSONObject("error").getJSONObject("message")
val firstKeyOfMessage = messageJson.keys().next().toString()
val errorMsgKey = errorResponseMapper.mapToDomainModel(responseBody).key
Resource.error(
firstKeyOfMessage,
null
errorMsgKey, null
)
} ?: returnUnknownError()
}
Expand All @@ -104,8 +105,7 @@ class AuthRepository @Inject constructor(

private fun returnUnknownError(): Resource<AuthToken> {
return Resource.error(
"Unknown error",
null
"Unknown error", null
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import com.example.speechbuddy.MainApplication.Companion.token_prefs
import com.example.speechbuddy.R
import com.example.speechbuddy.data.remote.models.ErrorResponseMapper
import com.example.speechbuddy.data.remote.requests.AuthSendCodeRequest
import com.example.speechbuddy.data.remote.requests.AuthVerifyEmailRequest
import com.example.speechbuddy.domain.models.AuthToken
Expand All @@ -33,6 +34,7 @@ class EmailVerificationViewModel @Inject internal constructor(

private val _uiState = MutableStateFlow(EmailVerificationUiState())
val uiState: StateFlow<EmailVerificationUiState> = _uiState.asStateFlow()
private val errorResponseMapper = ErrorResponseMapper()

var emailInput by mutableStateOf("")
private set
Expand Down Expand Up @@ -77,8 +79,6 @@ class EmailVerificationViewModel @Inject internal constructor(
}

fun verifySend(source: String?) {
/* TODO: 나중에 고쳐야 함 */

// What function(ultimately, API call) to use
val sendCode = if (source == "signup") {
repository::sendCodeForSignup
Expand Down Expand Up @@ -116,7 +116,8 @@ class EmailVerificationViewModel @Inject internal constructor(
}

400 -> {
val messageId = when (result.message()) {
val errorKey = errorResponseMapper.mapToDomainModel(result.errorBody()!!).key
val messageId = when (errorKey) {
"email" -> R.string.false_email
"already_taken" -> R.string.email_already_taken
"no_user" -> R.string.no_such_user
Expand Down
Loading

0 comments on commit 12118f2

Please sign in to comment.