Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle federation not enabled [WPB-5237] #2207

Merged
merged 4 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.wire.kalium.logic.functional.Either
import com.wire.kalium.network.exceptions.APINotSupported
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 @@ -154,6 +155,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 @@ -221,6 +223,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 @@ -19,7 +19,9 @@
package com.wire.kalium.logic.data.user

import co.touchlab.stately.collections.ConcurrentMutableMap
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 @@ -41,6 +43,7 @@ import com.wire.kalium.logic.di.MapperProvider
import com.wire.kalium.logic.failure.SelfUserDeleted
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 @@ -247,6 +250,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 @@ -37,6 +37,8 @@ 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.test_util.TestNetworkResponseError
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 @@ -181,6 +183,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 @@ -776,6 +798,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
Loading