Skip to content

Commit

Permalink
fix: handle federation not enabled [WPB-5237] (#2189)
Browse files Browse the repository at this point in the history
* fix: handle federation not enabled backend error
  • Loading branch information
Garzas authored Nov 8, 2023
1 parent 33a635a commit 9b8b0e6
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.network.exceptions.KaliumException
import com.wire.kalium.network.exceptions.isFederationDenied
import com.wire.kalium.network.exceptions.isFederationNotEnabled
import com.wire.kalium.network.utils.NetworkResponse
import io.ktor.utils.io.errors.IOException
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -147,6 +148,7 @@ sealed class NetworkFailure : CoreFailure {

data class General(val label: String) : FederatedBackendFailure()
data class FederationDenied(val label: String) : FederatedBackendFailure()
data class FederationNotEnabled(val label: String) : FederatedBackendFailure()

data class ConflictingBackends(override val domains: List<String>) : FederatedBackendFailure(), RetryableFailure

Expand Down Expand Up @@ -200,6 +202,8 @@ internal inline fun <T : Any> wrapApiRequest(networkCall: () -> NetworkResponse<
exception is KaliumException.FederationError -> {
if (exception.isFederationDenied()) {
Either.Left(NetworkFailure.FederatedBackendFailure.FederationDenied(exception.errorResponse.label))
} else if (exception.isFederationNotEnabled()) {
Either.Left(NetworkFailure.FederatedBackendFailure.FederationNotEnabled(exception.errorResponse.label))
} else {
Either.Left(NetworkFailure.FederatedBackendFailure.General(exception.errorResponse.label))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

package com.wire.kalium.logic.data.user

import com.wire.kalium.logger.obfuscateDomain
import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.NetworkFailure
import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.data.conversation.MemberMapper
import com.wire.kalium.logic.data.conversation.Recipient
Expand All @@ -42,6 +44,7 @@ import com.wire.kalium.logic.failure.SelfUserDeleted
import com.wire.kalium.logic.feature.SelfTeamIdProvider
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.flatMapLeft
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.functional.foldToEitherWhileRight
import com.wire.kalium.logic.functional.getOrNull
Expand Down Expand Up @@ -237,6 +240,24 @@ internal class UserDataSource internal constructor(
)
}
}
.flatMapLeft { error ->
if (error is NetworkFailure.FederatedBackendFailure.FederationNotEnabled) {
val domains = qualifiedUserIdList
.filterNot { it.domain == selfUserId.domain }
.map { it.domain.obfuscateDomain() }
.toSet()
val domainNames = domains.joinToString(separator = ", ")
kaliumLogger.e("User ids contains different domains when federation is not enabled by backend: $domainNames")
wrapApiRequest {
userDetailsApi.getMultipleUsers(
ListUserRequest.qualifiedIds(qualifiedUserIdList.filter { it.domain == selfUserId.domain }
.map { userId -> userId.toApi() })
)
}
} else {
Either.Left(error)
}
}
.flatMap { listUserProfileDTO ->
if (listUserProfileDTO.usersFailed.isNotEmpty()) {
kaliumLogger.d("Handling ${listUserProfileDTO.usersFailed.size} failed users")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import com.wire.kalium.logic.framework.TestUser.LIST_USERS_DTO
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.getOrNull
import com.wire.kalium.logic.sync.receiver.UserEventReceiverTest
import com.wire.kalium.logic.test_util.TestNetworkException.federationNotEnabled
import com.wire.kalium.logic.util.shouldFail
import com.wire.kalium.logic.util.shouldSucceed
import com.wire.kalium.network.api.base.authenticated.self.SelfApi
Expand Down Expand Up @@ -198,6 +199,26 @@ class UserRepositoryTest {
.wasNotInvoked()
}

@Test
fun givenAnUserIdListWithDifferentDomain_whenApiReturnsFederationDisabledError_thenShouldTryToFetchOnlyUsersWithSelfDomain() = runTest {
// given
val requestedUserIds = setOf(TestUser.OTHER_USER_ID, TestUser.OTHER_FEDERATED_USER_ID)
val (arrangement, userRepository) = Arrangement()
.withGetMultipleUsersApiRequestFederationNotEnabledError()
.arrange()
// when
userRepository.fetchUsersByIds(requestedUserIds).shouldFail()
// then
verify(arrangement.userDetailsApi)
.suspendFunction(arrangement.userDetailsApi::getMultipleUsers)
.with(eq(QualifiedUserIdListRequest(requestedUserIds.map { it.toApi() }.toList())))
.wasInvoked(exactly = once)
verify(arrangement.userDetailsApi)
.suspendFunction(arrangement.userDetailsApi::getMultipleUsers)
.with(eq(QualifiedUserIdListRequest(listOf(TestUser.OTHER_USER_ID.toApi()))))
.wasInvoked(exactly = once)
}

@Test
fun givenAnEmptyUserIdListFromSameDomainAsSelf_whenFetchingUsers_thenShouldNotFetchMultipleUsersAndSucceed() = runTest {
// given
Expand Down Expand Up @@ -696,6 +717,13 @@ class UserRepositoryTest {
.thenReturn(NetworkResponse.Success(result, mapOf(), HttpStatusCode.OK.value))
}

fun withGetMultipleUsersApiRequestFederationNotEnabledError() = apply {
given(userDetailsApi)
.suspendFunction(userDetailsApi::getMultipleUsers)
.whenInvokedWith(any())
.thenReturn(NetworkResponse.Error(federationNotEnabled))
}

fun withUpdateDisplayNameApiRequestResponse(response: NetworkResponse<Unit>) = apply {
given(selfApi)
.suspendFunction(selfApi::updateSelf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ object TestNetworkException {
val guestLinkDisables = KaliumException.InvalidRequestError(
ErrorResponse(409, "Guest links are disabled", "guest-links-disabled")
)

val federationNotEnabled = KaliumException.FederationError(
ErrorResponse(400, "no federator configured", "federation-not-enabled")
)
}

object TestNetworkResponseError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ data class ErrorResponse(
@SerialName("label") val label: String,
@SerialName("data") val cause: Cause? = null,
) {
fun isFederationError() = cause?.type == "federation"
fun isFederationError() = cause?.type == "federation" || label.contains("federation")
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.wire.kalium.network.exceptions.NetworkErrorLabel.BLACKLISTED_EMAIL
import com.wire.kalium.network.exceptions.NetworkErrorLabel.DOMAIN_BLOCKED_FOR_REGISTRATION
import com.wire.kalium.network.exceptions.NetworkErrorLabel.FEDERATION_DENIED
import com.wire.kalium.network.exceptions.NetworkErrorLabel.FEDERATION_FAILURE
import com.wire.kalium.network.exceptions.NetworkErrorLabel.FEDERATION_NOT_ENABLED
import com.wire.kalium.network.exceptions.NetworkErrorLabel.GUEST_LINKS_DISABLED
import com.wire.kalium.network.exceptions.NetworkErrorLabel.HANDLE_EXISTS
import com.wire.kalium.network.exceptions.NetworkErrorLabel.INVALID_CODE
Expand Down Expand Up @@ -224,3 +225,4 @@ val KaliumException.InvalidRequestError.authenticationCodeFailure: Authenticatio
}

fun KaliumException.FederationError.isFederationDenied() = errorResponse.label == FEDERATION_DENIED
fun KaliumException.FederationError.isFederationNotEnabled() = errorResponse.label == FEDERATION_NOT_ENABLED
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,18 @@ internal object NetworkErrorLabel {
const val MLS_KEY_PACKAGE_REF_NOT_FOUND = "mls-key-package-ref-not-found"
const val MLS_MISSING_GROUP_INFO = "mls-missing-group-info"
const val UNKNOWN_CLIENT = "unknown-client"
const val FEDERATION_FAILURE = "federation-remote-error"
const val NOT_TEAM_MEMBER = "no-team-member"
const val NO_CONVERSATION = "no-conversation"
const val NO_CONVERSATION_CODE = "no-conversation-code"
const val GUEST_LINKS_DISABLED = "guest-links-disabled"
const val ACCESS_DENIED = "access-denied"
const val WRONG_CONVERSATION_PASSWORD = "invalid-conversation-password"
const val FEDERATION_UNREACHABLE_DOMAINS = "federation-unreachable-domains-error"

// Federation
const val FEDERATION_FAILURE = "federation-remote-error"
const val FEDERATION_DENIED = "federation-denied"
const val FEDERATION_NOT_ENABLED = "federation-not-enabled"
const val FEDERATION_UNREACHABLE_DOMAINS = "federation-unreachable-domains-error"

object KaliumCustom {
const val MISSING_REFRESH_TOKEN = "missing-refresh_token"
Expand Down

0 comments on commit 9b8b0e6

Please sign in to comment.