Skip to content

Commit

Permalink
feat: handle reason for conversation.member-leave event [WPB-5877] (#…
Browse files Browse the repository at this point in the history
…2307)

* feat: handle reason for conversation.member-leave event

* feat: add a new team member removed system message

* detekt

* detekt

* detekt

* fix test

* test

* even more tests

* mote test
  • Loading branch information
MohamadJaara authored Dec 18, 2023
1 parent 7252a63 commit 97c7296
Show file tree
Hide file tree
Showing 35 changed files with 654 additions and 316 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ sealed class Event(open val id: String, open val transient: Boolean, open val li
override val live: Boolean,
val removedBy: UserId,
val removedList: List<UserId>,
val timestampIso: String
val timestampIso: String,
val reason: MemberLeaveReason
) : Conversation(id, transient, live, conversationId) {

override fun toLogMap(): Map<String, Any?> = mapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.wire.kalium.logic.data.user.toModel
import com.wire.kalium.logic.di.MapperProvider
import com.wire.kalium.logic.util.Base64
import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigData
import com.wire.kalium.network.api.base.authenticated.notification.MemberLeaveReasonDTO
import com.wire.kalium.network.api.base.authenticated.notification.EventContentDTO
import com.wire.kalium.network.api.base.authenticated.notification.EventResponse
import com.wire.kalium.network.api.base.authenticated.properties.PropertiesApi.PropertyKey.WIRE_RECEIPT_MODE
Expand Down Expand Up @@ -475,10 +476,11 @@ class EventMapper(
id = id,
conversationId = eventContentDTO.qualifiedConversation.toModel(),
removedBy = eventContentDTO.qualifiedFrom.toModel(),
removedList = eventContentDTO.members.qualifiedUserIds.map { it.toModel() },
removedList = eventContentDTO.removedUsers.qualifiedUserIds.map { it.toModel() },
timestampIso = eventContentDTO.time,
transient = transient,
live = live,
reason = eventContentDTO.removedUsers.reason.toModel()
)

private fun memberUpdate(
Expand Down Expand Up @@ -702,3 +704,9 @@ class EventMapper(
)

}

private fun MemberLeaveReasonDTO.toModel(): MemberLeaveReason = when (this) {
MemberLeaveReasonDTO.LEFT -> MemberLeaveReason.Left
MemberLeaveReasonDTO.REMOVED -> MemberLeaveReason.Removed
MemberLeaveReasonDTO.USER_DELETED -> MemberLeaveReason.UserDeleted
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.logic.data.event

sealed interface MemberLeaveReason {
object Left : MemberLeaveReason
object Removed : MemberLeaveReason
object UserDeleted : MemberLeaveReason
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ sealed class MessageContent {
sealed class MemberChange(open val members: List<UserId>) : System() {
data class Added(override val members: List<UserId>) : MemberChange(members)
data class Removed(override val members: List<UserId>) : MemberChange(members)
data class RemovedFromTeam(override val members: List<UserId>) : MemberChange(members)
data class FailedToAdd(override val members: List<UserId>) : MemberChange(members)
data class CreationAdded(override val members: List<UserId>) : MemberChange(members)
data class FederationRemoved(override val members: List<UserId>) : MemberChange(members)
Expand All @@ -243,6 +244,7 @@ sealed class MessageContent {

data class ConversationRenamed(val conversationName: String) : System()

@Deprecated("Use MemberChange.RemovedFromTeam instead")
data class TeamMemberRemoved(val userName: String) : System()

data object MissedCall : System()
Expand Down Expand Up @@ -356,9 +358,9 @@ fun MessageContent?.getType() = when (this) {
is MessageContent.HistoryLostProtocolChanged -> "HistoryLostProtocolChanged"
is MessageContent.MemberChange.Added -> "MemberChange.Added"
is MessageContent.MemberChange.Removed -> "MemberChange.Removed"
is MessageContent.MemberChange.RemovedFromTeam -> "MemberChange.RemovedFromTeam"
is MessageContent.MissedCall -> "MissedCall"
is MessageContent.NewConversationReceiptMode -> "NewConversationReceiptMode"
is MessageContent.TeamMemberRemoved -> "TeamMemberRemoved"
is MessageContent.ConversationCreated -> "ConversationCreated"
is MessageContent.MemberChange.CreationAdded -> "MemberChange.CreationAdded"
is MessageContent.MemberChange.FailedToAdd -> "MemberChange.FailedToAdd"
Expand All @@ -377,6 +379,7 @@ fun MessageContent?.getType() = when (this) {
MessageContent.ConversationVerifiedProteus -> "ConversationVerification.Verified.Proteus"
is MessageContent.ConversationStartedUnverifiedWarning -> "ConversationStartedUnverifiedWarning"
is MessageContent.Location -> "Location"
is MessageContent.TeamMemberRemoved -> "TeamMemberRemoved"
is MessageContent.LegalHold.ForConversation.Disabled -> "LegalHold.ForConversation.Disabled"
is MessageContent.LegalHold.ForConversation.Enabled -> "LegalHold.ForConversation.Enabled"
is MessageContent.LegalHold.ForMembers.Disabled -> "LegalHold.ForMembers.Disabled"
Expand Down Expand Up @@ -412,7 +415,13 @@ sealed interface MessagePreviewContent {
val otherUserIdList: List<UserId> // TODO add usernames
) : WithUser

data class MembersRemoved(
data class ConversationMembersRemoved(
override val username: String?,
val isSelfUserRemoved: Boolean,
val otherUserIdList: List<UserId> // TODO add usernames
) : WithUser

data class TeamMembersRemoved(
override val username: String?,
val isSelfUserRemoved: Boolean,
val otherUserIdList: List<UserId> // TODO add usernames
Expand All @@ -432,6 +441,7 @@ sealed interface MessagePreviewContent {

data class ConversationNameChange(override val username: String?) : WithUser

@Deprecated("Use WithUser.TeamMembersRemoved instead")
data class TeamMemberRemoved(override val username: String?) : WithUser

data class MissedCall(override val username: String?) : WithUser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ class MessageMapperImpl(
message.date,
LocalNotificationCommentType.LOCATION
)

MessageEntity.ContentType.MEMBER_CHANGE -> null
MessageEntity.ContentType.RESTRICTED_ASSET -> null
MessageEntity.ContentType.CONVERSATION_RENAMED -> null
Expand Down Expand Up @@ -383,17 +384,18 @@ class MessageMapperImpl(
}

@Suppress("ComplexMethod")
fun MessageEntityContent.System.toMessageContent(): MessageContent.System = when (this) {
is MessageEntityContent.MemberChange -> {
val memberList = this.memberUserIdList.map { it.toModel() }
when (this.memberChangeType) {
MessageEntity.MemberChangeType.ADDED -> MessageContent.MemberChange.Added(memberList)
MessageEntity.MemberChangeType.REMOVED -> MessageContent.MemberChange.Removed(memberList)
MessageEntity.MemberChangeType.CREATION_ADDED -> MessageContent.MemberChange.CreationAdded(memberList)
MessageEntity.MemberChangeType.FAILED_TO_ADD -> MessageContent.MemberChange.FailedToAdd(memberList)
MessageEntity.MemberChangeType.FEDERATION_REMOVED -> MessageContent.MemberChange.FederationRemoved(memberList)
fun MessageEntityContent.System.toMessageContent(): MessageContent.System = when (this) {
is MessageEntityContent.MemberChange -> {
val memberList = this.memberUserIdList.map { it.toModel() }
when (this.memberChangeType) {
MessageEntity.MemberChangeType.ADDED -> MessageContent.MemberChange.Added(memberList)
MessageEntity.MemberChangeType.REMOVED -> MessageContent.MemberChange.Removed(memberList)
MessageEntity.MemberChangeType.CREATION_ADDED -> MessageContent.MemberChange.CreationAdded(memberList)
MessageEntity.MemberChangeType.FAILED_TO_ADD -> MessageContent.MemberChange.FailedToAdd(memberList)
MessageEntity.MemberChangeType.FEDERATION_REMOVED -> MessageContent.MemberChange.FederationRemoved(memberList)
MessageEntity.MemberChangeType.REMOVED_FROM_TEAM -> MessageContent.MemberChange.RemovedFromTeam(memberList)
}
}
}

is MessageEntityContent.MissedCall -> MessageContent.MissedCall
is MessageEntityContent.ConversationRenamed -> MessageContent.ConversationRenamed(conversationName)
Expand Down Expand Up @@ -454,7 +456,7 @@ private fun MessagePreviewEntityContent.toMessageContent(): MessagePreviewConten
otherUserIdList = otherUserIdList.map { it.toModel() }
)

is MessagePreviewEntityContent.MembersRemoved -> MessagePreviewContent.WithUser.MembersRemoved(
is MessagePreviewEntityContent.ConversationMembersRemoved -> MessagePreviewContent.WithUser.ConversationMembersRemoved(
username = senderName,
isSelfUserRemoved = isContainSelfUserId,
otherUserIdList = otherUserIdList.map { it.toModel() }
Expand All @@ -477,11 +479,17 @@ private fun MessagePreviewEntityContent.toMessageContent(): MessagePreviewConten
otherUserIdList = otherUserIdList.map { it.toModel() }
)

is MessagePreviewEntityContent.TeamMembersRemoved -> MessagePreviewContent.WithUser.TeamMembersRemoved(
username = senderName,
isSelfUserRemoved = isContainSelfUserId,
otherUserIdList = otherUserIdList.map { it.toModel() }
)

is MessagePreviewEntityContent.Ephemeral -> MessagePreviewContent.Ephemeral(isGroupConversation)
is MessagePreviewEntityContent.MentionedSelf -> MessagePreviewContent.WithUser.MentionedSelf(senderName)
is MessagePreviewEntityContent.MissedCall -> MessagePreviewContent.WithUser.MissedCall(senderName)
is MessagePreviewEntityContent.QuotedSelf -> MessagePreviewContent.WithUser.QuotedSelf(senderName)
is MessagePreviewEntityContent.TeamMemberRemoved -> MessagePreviewContent.WithUser.TeamMemberRemoved(userName)
is MessagePreviewEntityContent.TeamMemberRemoved_Legacy -> MessagePreviewContent.WithUser.TeamMemberRemoved(userName)
is MessagePreviewEntityContent.Text -> MessagePreviewContent.WithUser.Text(username = senderName, messageBody = messageBody)
is MessagePreviewEntityContent.CryptoSessionReset -> MessagePreviewContent.CryptoSessionReset
MessagePreviewEntityContent.Unknown -> MessagePreviewContent.Unknown
Expand Down Expand Up @@ -623,6 +631,10 @@ fun MessageContent.System.toMessageEntityContent(): MessageEntityContent.System
MessageEntity.MemberChangeType.FEDERATION_REMOVED
)

is MessageContent.MemberChange.RemovedFromTeam -> MessageEntityContent.MemberChange(
memberUserIdList,
MessageEntity.MemberChangeType.REMOVED_FROM_TEAM
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ internal class PersistMessageUseCaseImpl(
is MessageContent.Reaction -> false
is MessageContent.Cleared -> false
is MessageContent.ConversationRenamed -> true
is MessageContent.TeamMemberRemoved -> false
is MessageContent.Receipt -> false
is MessageContent.ClientAction -> false
is MessageContent.CryptoSessionReset -> false
Expand All @@ -112,5 +111,7 @@ internal class PersistMessageUseCaseImpl(
is MessageContent.ConversationStartedUnverifiedWarning -> false
is MessageContent.Location -> true
is MessageContent.LegalHold -> false
is MessageContent.MemberChange.RemovedFromTeam -> false
is MessageContent.TeamMemberRemoved -> false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.id.NetworkQualifiedId
import com.wire.kalium.logic.data.id.QualifiedID
import com.wire.kalium.logic.data.id.SelfTeamIdProvider
import com.wire.kalium.logic.data.id.TeamId
import com.wire.kalium.logic.data.id.toApi
import com.wire.kalium.logic.data.id.toDao
import com.wire.kalium.logic.data.id.toModel
Expand Down Expand Up @@ -112,6 +113,8 @@ interface UserRepository {
suspend fun updateUserFromEvent(event: Event.User.Update): Either<CoreFailure, Unit>
suspend fun markUserAsDeletedAndRemoveFromGroupConversations(userId: UserId): Either<CoreFailure, Unit>

suspend fun markUserAsDeletedAndRemoveFromGroupConversations(userId: List<UserId>): Either<CoreFailure, Unit>

/**
* Marks federated user as defederated in order to hold conversation history
* when backends stops federating.
Expand Down Expand Up @@ -140,6 +143,8 @@ interface UserRepository {
suspend fun updateSupportedProtocols(protocols: Set<SupportedProtocol>): Either<CoreFailure, Unit>

suspend fun updateActiveOneOnOneConversation(userId: UserId, conversationId: ConversationId): Either<CoreFailure, Unit>

suspend fun isAtLeastOneUserATeamMember(userId: List<UserId>, teamId: TeamId): Either<StorageFailure, Boolean>
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand Down Expand Up @@ -316,8 +321,8 @@ internal class UserDataSource internal constructor(
userProfile = userProfileDTO,
connectionState = ConnectionEntity.State.ACCEPTED,
userTypeEntity =
if (userProfileDTO.service != null) UserTypeEntity.SERVICE
else userTypeEntityMapper.teamRoleCodeToUserType(mapTeamMemberDTO[userProfileDTO.id.value]?.permissions?.own)
if (userProfileDTO.service != null) UserTypeEntity.SERVICE
else userTypeEntityMapper.teamRoleCodeToUserType(mapTeamMemberDTO[userProfileDTO.id.value]?.permissions?.own)
)
}
val otherUsers = listUserProfileDTO
Expand Down Expand Up @@ -461,6 +466,10 @@ internal class UserDataSource internal constructor(
override suspend fun updateActiveOneOnOneConversation(userId: UserId, conversationId: ConversationId): Either<CoreFailure, Unit> =
wrapStorageRequest { userDAO.updateActiveOneOnOneConversation(userId.toDao(), conversationId.toDao()) }

override suspend fun isAtLeastOneUserATeamMember(userId: List<UserId>, teamId: TeamId) = wrapStorageRequest {
userDAO.isAtLeastOneUserATeamMember(userId.map { it.toDao() }, teamId.value)
}

override fun observeAllKnownUsersNotInConversation(
conversationId: ConversationId
): Flow<Either<StorageFailure, List<OtherUser>>> {
Expand Down Expand Up @@ -502,12 +511,15 @@ internal class UserDataSource internal constructor(
}
}

override suspend fun markUserAsDeletedAndRemoveFromGroupConversations(userId: UserId): Either<CoreFailure, Unit> {
return wrapStorageRequest {
userDAO.markUserAsDeletedAndRemoveFromGroupConv(userId.toDao())
}
override suspend fun markUserAsDeletedAndRemoveFromGroupConversations(userId: UserId): Either<CoreFailure, Unit> = wrapStorageRequest {
userDAO.markUserAsDeletedAndRemoveFromGroupConv(userId.toDao())
}

override suspend fun markUserAsDeletedAndRemoveFromGroupConversations(userId: List<UserId>): Either<CoreFailure, Unit> =
wrapStorageRequest {
userDAO.markUserAsDeletedAndRemoveFromGroupConv(userId.map { it.toDao() })
}

override suspend fun defederateUser(userId: UserId): Either<CoreFailure, Unit> {
return wrapStorageRequest {
userDAO.markUserAsDefederated(userId.toDao())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1164,9 +1164,10 @@ class UserSessionScope internal constructor(
federatedIdMapper = federatedIdMapper
)

private val updateConversationClientsForCurrentCall: Lazy<UpdateConversationClientsForCurrentCallUseCase> = lazy {
UpdateConversationClientsForCurrentCallUseCaseImpl(callRepository, conversationClientsInCallUpdater)
}
private val updateConversationClientsForCurrentCall: Lazy<UpdateConversationClientsForCurrentCallUseCase>
get() = lazy {
UpdateConversationClientsForCurrentCallUseCaseImpl(callRepository, conversationClientsInCallUpdater)
}

private val reactionRepository = ReactionRepositoryImpl(userId, userStorage.database.reactionDAO)
private val receiptRepository = ReceiptRepositoryImpl(userStorage.database.receiptDAO)
Expand Down Expand Up @@ -1276,7 +1277,7 @@ class UserSessionScope internal constructor(
)
private val memberLeaveHandler: MemberLeaveEventHandler
get() = MemberLeaveEventHandlerImpl(
userStorage.database.memberDAO, userRepository, persistMessage, updateConversationClientsForCurrentCall
userStorage.database.memberDAO, userRepository, persistMessage, updateConversationClientsForCurrentCall, selfTeamId
)
private val memberChangeHandler: MemberChangeEventHandler
get() = MemberChangeEventHandlerImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal class TeamEventReceiverImpl(
}
// TODO: Make sure errors are accounted for by each handler.
// onEvent now requires Either, so we can propagate errors,
// but not all handlers are using it yet.®
// but not all handlers are using it yet.
// Returning Either.Right is the equivalent of how it was originally working.
return Either.Right(Unit)
}
Expand Down
Loading

0 comments on commit 97c7296

Please sign in to comment.