diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationGroupRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationGroupRepository.kt index 1f3cdda20f2..0cb58e990fa 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationGroupRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationGroupRepository.kt @@ -30,6 +30,7 @@ import com.wire.kalium.logic.data.id.toApi import com.wire.kalium.logic.data.id.toDao import com.wire.kalium.logic.data.id.toModel import com.wire.kalium.logic.data.message.MessageContent.MemberChange.FailedToAdd +import com.wire.kalium.logic.data.mls.CipherSuite import com.wire.kalium.logic.data.service.ServiceId import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.UserRepository @@ -130,31 +131,21 @@ internal class ConversationGroupRepositoryImpl( } when (apiResult) { - is Either.Left -> { - val canRetryOnce = apiResult.value.hasUnreachableDomainsError && lastUsersAttempt is LastUsersAttempt.None - if (canRetryOnce) { - extractValidUsersForRetryableError(apiResult.value, usersList) - .flatMap { (validUsers, failedUsers, failType) -> - // edge case, in case backend goes 🍌 and returns non-matching domains - if (failedUsers.isEmpty()) Either.Left(apiResult.value) - } - } - } - is Either.Right -> { - handleCreateConversationSuccess( - apiResult, - usersList, - failedUsersList, - selfTeamId - ) - } + is Either.Left -> handleCreateConverstionFailure( + apiResult = apiResult, + usersList = usersList, + name = name, + options = options, + lastUsersAttempt = lastUsersAttempt + ) is Either.Right -> handleGroupConversationCreated(apiResult.value, selfTeamId, usersList, lastUsersAttempt) } } - private suspend fun handleCreateConversationSuccess( - apiResult: Either.Right, + private suspend fun handleGroupConversationCreated( + conversationResponse: ConversationResponse, + selfTeamId: TeamId?, usersList: List, lastUsersAttempt: LastUsersAttempt, ): Either { @@ -184,7 +175,9 @@ internal class ConversationGroupRepositoryImpl( Either.Right(Unit) } else { newGroupConversationSystemMessagesCreator.value.conversationFailedToAddMembers( - conversationEntity.id.toModel(), protocolSpecificAdditionFailures.toList(), FailedToAdd.Type.Unknown + conversationId = conversationEntity.id.toModel(), + userIdList = protocolSpecificAdditionFailures.toList(), + type = FailedToAdd.Type.Federation ) } }.flatMap { @@ -211,6 +204,27 @@ internal class ConversationGroupRepositoryImpl( } } + private suspend fun handleCreateConverstionFailure( + apiResult: Either.Left, + usersList: List, + name: String?, + options: ConversationOptions, + lastUsersAttempt: LastUsersAttempt + ): Either { + val canRetryOnce = apiResult.value.hasUnreachableDomainsError && lastUsersAttempt is LastUsersAttempt.None + return if (canRetryOnce) { + extractValidUsersForRetryableError(apiResult.value, usersList) + .flatMap { (validUsers, failedUsers, failType) -> + // edge case, in case backend goes 🍌 and returns non-matching domains + if (failedUsers.isEmpty()) Either.Left(apiResult.value) + + createGroupConversation(name, validUsers, options, LastUsersAttempt.Failed(failedUsers, failType)) + } + } else { + Either.Left(apiResult.value) + } + } + override suspend fun addMembers( userIdList: List, conversationId: ConversationId @@ -225,11 +239,21 @@ internal class ConversationGroupRepositoryImpl( tryAddMembersToCloudAndStorage(userIdList, conversationId, LastUsersAttempt.None) .flatMap { // best effort approach for migrated conversations, no retries - mlsConversationRepository.addMemberToMLSGroup(GroupID(protocol.groupId), userIdList) + mlsConversationRepository.addMemberToMLSGroup( + GroupID(protocol.groupId), + userIdList, + CipherSuite.fromTag(protocol.cipherSuite.cipherSuiteTag) + ) } is ConversationEntity.ProtocolInfo.MLS -> { - tryAddMembersToMLSGroup(conversationId, protocol.groupId, userIdList, LastUsersAttempt.None) + tryAddMembersToMLSGroup( + conversationId, + protocol.groupId, + userIdList, + LastUsersAttempt.None, + cipherSuite = CipherSuite.fromTag(protocol.cipherSuite.cipherSuiteTag) + ) } } } @@ -238,14 +262,22 @@ internal class ConversationGroupRepositoryImpl( * Handle the error cases and retry for claimPackages offline and out of packages. * Handle error case and retry for sendingCommit unreachable or missing legal hold consent. */ + @Suppress("LongMethod") private suspend fun tryAddMembersToMLSGroup( conversationId: ConversationId, groupId: String, userIdList: List, lastUsersAttempt: LastUsersAttempt, + cipherSuite: CipherSuite, remainingAttempts: Int = 2 ): Either { - return when (val addingMemberResult = mlsConversationRepository.addMemberToMLSGroup(GroupID(groupId), userIdList)) { + return when ( + val addingMemberResult = mlsConversationRepository.addMemberToMLSGroup( + GroupID(groupId), + userIdList, + cipherSuite + ) + ) { is Either.Right -> handleMLSMembersNotAdded(conversationId, lastUsersAttempt) is Either.Left -> { addingMemberResult.value.handleMLSMembersFailed( @@ -254,17 +286,20 @@ internal class ConversationGroupRepositoryImpl( userIdList = userIdList, lastUsersAttempt = lastUsersAttempt, remainingAttempts = remainingAttempts, + cipherSuite = cipherSuite ) } } } + @Suppress("LongMethod") private suspend fun CoreFailure.handleMLSMembersFailed( conversationId: ConversationId, groupId: String, userIdList: List, lastUsersAttempt: LastUsersAttempt, remainingAttempts: Int, + cipherSuite: CipherSuite ): Either { return when { // claiming key packages offline or out of packages @@ -278,7 +313,8 @@ internal class ConversationGroupRepositoryImpl( failedUsers = lastUsersAttempt.failedUsers + failedUsers, failType = FailedToAdd.Type.Federation, ), - remainingAttempts = remainingAttempts - 1 + remainingAttempts = remainingAttempts - 1, + cipherSuite = cipherSuite ) } @@ -293,7 +329,8 @@ internal class ConversationGroupRepositoryImpl( failedUsers = lastUsersAttempt.failedUsers + failedUsers, failType = FailedToAdd.Type.Federation, ), - remainingAttempts = remainingAttempts - 1 + remainingAttempts = remainingAttempts - 1, + cipherSuite = cipherSuite ) } @@ -309,7 +346,8 @@ internal class ConversationGroupRepositoryImpl( failedUsers = lastUsersAttempt.failedUsers + failedUsers, failType = FailedToAdd.Type.LegalHold, ), - remainingAttempts = remainingAttempts - 1 + remainingAttempts = remainingAttempts - 1, + cipherSuite = cipherSuite ) } } @@ -430,7 +468,7 @@ internal class ConversationGroupRepositoryImpl( } } } else { - val failType = (lastUsersAttempt as? LastUsersAttempt.Failed)?.failType ?: FailedToAdd.Type.Unknown + val failType = apiResult.value.toFailedToAddType() newGroupConversationSystemMessagesCreator.value.conversationFailedToAddMembers( conversationId, userIdList + lastUsersAttempt.failedUsers, failType ).flatMap { @@ -480,7 +518,11 @@ internal class ConversationGroupRepositoryImpl( is ConversationEntity.ProtocolInfo.MLSCapable -> { joinExistingMLSConversation(conversationId).flatMap { - mlsConversationRepository.addMemberToMLSGroup(GroupID(protocol.groupId), listOf(selfUserId)) + mlsConversationRepository.addMemberToMLSGroup( + GroupID(protocol.groupId), + listOf(selfUserId), + CipherSuite.fromTag(protocol.cipherSuite.cipherSuiteTag) + ) } } } @@ -600,6 +642,12 @@ internal class ConversationGroupRepositoryImpl( Either.Right(ValidToInvalidUsers(userIdList, emptyList(), FailedToAdd.Type.Unknown)) } + private fun CoreFailure.toFailedToAddType() = when { + this is NetworkFailure.FederatedBackendFailure -> FailedToAdd.Type.Federation + this.isMissingLegalHoldConsentError -> FailedToAdd.Type.LegalHold + else -> FailedToAdd.Type.Unknown + } + /** * Filter the initial [userIdList] into valid and invalid users where valid users are only team members. */