PM-15025: Update sendVerificationEmail to handle error responses (#4336)

This commit is contained in:
David Perez 2024-11-19 14:03:07 -06:00 committed by GitHub
parent d418444dc0
commit 5ea17700b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 62 additions and 10 deletions

View file

@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson
@ -68,7 +69,7 @@ interface IdentityService {
*/ */
suspend fun sendVerificationEmail( suspend fun sendVerificationEmail(
body: SendVerificationEmailRequestJson, body: SendVerificationEmailRequestJson,
): Result<String?> ): Result<SendVerificationEmailResponseJson>
/** /**
* Register a new account to Bitwarden using email verification flow. * Register a new account to Bitwarden using email verification flow.

View file

@ -11,6 +11,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson
@ -132,11 +133,20 @@ class IdentityServiceImpl(
override suspend fun sendVerificationEmail( override suspend fun sendVerificationEmail(
body: SendVerificationEmailRequestJson, body: SendVerificationEmailRequestJson,
): Result<String?> { ): Result<SendVerificationEmailResponseJson> {
return unauthenticatedIdentityApi return unauthenticatedIdentityApi
.sendVerificationEmail(body = body) .sendVerificationEmail(body = body)
.toResult() .toResult()
.map { it?.content } .map { SendVerificationEmailResponseJson.Success(it?.content) }
.recoverCatching { throwable ->
throwable
.toBitwardenError()
.parseErrorBodyOrNull<SendVerificationEmailResponseJson.Invalid>(
code = 400,
json = json,
)
?: throw throwable
}
} }
override suspend fun verifyEmailRegistrationToken( override suspend fun verifyEmailRegistrationToken(

View file

@ -22,6 +22,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJs
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
@ -1282,7 +1283,15 @@ class AuthRepositoryImpl(
) )
.fold( .fold(
onSuccess = { onSuccess = {
SendVerificationEmailResult.Success(it) when (it) {
is SendVerificationEmailResponseJson.Invalid -> {
SendVerificationEmailResult.Error(it.message)
}
is SendVerificationEmailResponseJson.Success -> {
SendVerificationEmailResult.Success(it.emailVerificationToken)
}
}
}, },
onFailure = { onFailure = {
SendVerificationEmailResult.Error(null) SendVerificationEmailResult.Error(null)

View file

@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
@ -370,14 +371,19 @@ class IdentityServiceTest : BaseServiceTest() {
runTest { runTest {
server.enqueue(MockResponse().setResponseCode(200).setBody(EMAIL_TOKEN)) server.enqueue(MockResponse().setResponseCode(200).setBody(EMAIL_TOKEN))
val result = identityService.sendVerificationEmail(SEND_VERIFICATION_EMAIL_REQUEST) val result = identityService.sendVerificationEmail(SEND_VERIFICATION_EMAIL_REQUEST)
assertEquals(JsonPrimitive(EMAIL_TOKEN).content.asSuccess(), result) assertEquals(
SendVerificationEmailResponseJson
.Success(JsonPrimitive(EMAIL_TOKEN).content)
.asSuccess(),
result,
)
} }
@Test @Test
fun `sendVerificationEmail should return null when response is empty success`() = runTest { fun `sendVerificationEmail should return null when response is empty success`() = runTest {
server.enqueue(MockResponse().setResponseCode(204)) server.enqueue(MockResponse().setResponseCode(204))
val result = identityService.sendVerificationEmail(SEND_VERIFICATION_EMAIL_REQUEST) val result = identityService.sendVerificationEmail(SEND_VERIFICATION_EMAIL_REQUEST)
assertEquals(null.asSuccess(), result) assertEquals(SendVerificationEmailResponseJson.Success(null).asSuccess(), result)
} }
@Test @Test
@ -422,7 +428,6 @@ class IdentityServiceTest : BaseServiceTest() {
) )
} }
@Suppress("MaxLineLength")
@Test @Test
fun `verifyEmailToken should return Invalid when response message is non expired error`() = fun `verifyEmailToken should return Invalid when response message is non expired error`() =
runTest { runTest {

View file

@ -38,6 +38,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJs
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
@ -6043,7 +6044,7 @@ class AuthRepositoryTest {
receiveMarketingEmails = true, receiveMarketingEmails = true,
), ),
) )
} returns EMAIL_VERIFICATION_TOKEN.asSuccess() } returns SendVerificationEmailResponseJson.Success(EMAIL_VERIFICATION_TOKEN).asSuccess()
val result = repository.sendVerificationEmail( val result = repository.sendVerificationEmail(
email = EMAIL, email = EMAIL,
@ -6056,6 +6057,32 @@ class AuthRepositoryTest {
) )
} }
@Test
fun `sendVerificationEmail success with invalid email should return error`() = runTest {
val errorMessage = "Failure"
coEvery {
identityService.sendVerificationEmail(
SendVerificationEmailRequestJson(
email = EMAIL,
name = NAME,
receiveMarketingEmails = true,
),
)
} returns SendVerificationEmailResponseJson
.Invalid(invalidMessage = errorMessage, validationErrors = null)
.asSuccess()
val result = repository.sendVerificationEmail(
email = EMAIL,
name = NAME,
receiveMarketingEmails = true,
)
assertEquals(
SendVerificationEmailResult.Error(errorMessage = errorMessage),
result,
)
}
@Test @Test
fun `sendVerificationEmail failure should return success if body null`() = runTest { fun `sendVerificationEmail failure should return success if body null`() = runTest {
coEvery { coEvery {
@ -6066,7 +6093,7 @@ class AuthRepositoryTest {
receiveMarketingEmails = true, receiveMarketingEmails = true,
), ),
) )
} returns null.asSuccess() } returns SendVerificationEmailResponseJson.Success(null).asSuccess()
val result = repository.sendVerificationEmail( val result = repository.sendVerificationEmail(
email = EMAIL, email = EMAIL,
@ -6089,7 +6116,7 @@ class AuthRepositoryTest {
receiveMarketingEmails = true, receiveMarketingEmails = true,
), ),
) )
} returns EMAIL_VERIFICATION_TOKEN.asSuccess() } returns SendVerificationEmailResponseJson.Success(EMAIL_VERIFICATION_TOKEN).asSuccess()
val result = repository.sendVerificationEmail( val result = repository.sendVerificationEmail(
email = EMAIL, email = EMAIL,