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

feat(federation): offline backends handling (epic) #1548

Merged
merged 54 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bb51400
Revert "feat: feature federation msg failed to send handling (AR-3015…
yamilmedina Mar 2, 2023
dc7cd41
chore(migration): out of conflict
yamilmedina Mar 3, 2023
ae00672
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Mar 3, 2023
78ee769
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Mar 15, 2023
bc7a9a4
chore: more code documentation
yamilmedina Mar 15, 2023
49c2052
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Mar 17, 2023
2966b94
chore: recover small improv for uneeded call
yamilmedina Mar 15, 2023
cea803a
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Mar 27, 2023
eac76fd
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 3, 2023
6ad3412
chore: detekt
yamilmedina Apr 3, 2023
77b3933
fix: sq migration updated after a really long time =/
yamilmedina Apr 5, 2023
1df5c6a
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 6, 2023
59200d2
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 12, 2023
7d27bd5
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 17, 2023
c5bad34
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 19, 2023
4cdc0b4
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 24, 2023
0541066
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 24, 2023
ce6f592
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 24, 2023
5bba4d3
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Apr 25, 2023
2cea133
feat: offline backends - handling list-prekeys failed to list (AR-317…
yamilmedina Apr 28, 2023
31f234c
chore: tmp for merge
yamilmedina May 2, 2023
8a25296
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina May 2, 2023
ddd7c3f
feat: wip, merge sqw
yamilmedina May 9, 2023
7ab0e3d
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina May 9, 2023
2c3998d
feat: offline backends - users metadata (AR-3124) (#1698)
yamilmedina May 10, 2023
53114bd
feat(offline-backends): users and conversations without metadata refe…
yamilmedina May 19, 2023
aa362bf
feat(offline-backends): users and conversations without metadata refe…
yamilmedina May 22, 2023
43b0ad8
chore: wip for merge
yamilmedina May 23, 2023
e0225ce
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina May 23, 2023
f872ac8
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina May 23, 2023
213ae3a
fix: handle edge case error when no sessions
yamilmedina May 24, 2023
9ef6481
fix: tests
yamilmedina May 24, 2023
439f95e
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina May 25, 2023
6a027fb
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina May 26, 2023
67bb896
feat: wip merge
yamilmedina Jun 1, 2023
e3e9ff8
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 1, 2023
83684a2
feat: wip merge
yamilmedina Jun 1, 2023
0da5c40
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 1, 2023
a98b5fc
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 2, 2023
450a89d
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 2, 2023
e074d11
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 4, 2023
79891d2
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 6, 2023
9d66082
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 7, 2023
8ecc1e1
feat: conversation creation with offline backends (WPB-364) (#1774)
yamilmedina Jun 7, 2023
57cf57a
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 12, 2023
42cb85f
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 16, 2023
0aa449d
chore: preparing for rebase
yamilmedina Jun 20, 2023
c64e3fa
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 20, 2023
41495f7
chore: preparing merge
yamilmedina Jun 22, 2023
3ff1d3f
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 22, 2023
fd1b114
chore: preparing merge, better naming for func
yamilmedina Jun 22, 2023
4814486
chore: preparing merge, coverage
yamilmedina Jun 22, 2023
bbd62a0
chore: preparing merge, coverage
yamilmedina Jun 22, 2023
2096232
Merge branch 'develop' into feat/epic-federation-offline-messages
yamilmedina Jun 23, 2023
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
15 changes: 15 additions & 0 deletions logic/src/commonMain/kotlin/com/wire/kalium/logic/CoreFailure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,21 @@ internal inline fun <T : Any> wrapStorageRequest(storageRequest: () -> T?): Eith
}
}

/**
* Wrap a storage request with a custom error handler that let's delegate the error handling to the caller.
*/
@Suppress("TooGenericExceptionCaught")
internal inline fun <T : Any> wrapStorageRequest(
noinline errorHandler: (Exception) -> Either<StorageFailure, T>,
storageRequest: () -> T?
): Either<StorageFailure, T> {
return try {
storageRequest()?.let { data -> Either.Right(data) } ?: Either.Left(StorageFailure.DataNotFound)
} catch (exception: Exception) {
errorHandler(exception)
}
}

internal inline fun <T : Any> wrapStorageNullableRequest(storageRequest: () -> T?): Either<StorageFailure, T?> {
return try {
storageRequest().let { data -> Either.Right(data) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.wire.kalium.logic.data.conversation

import com.wire.kalium.logic.data.connection.ConnectionStatusMapper
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.id.NetworkQualifiedId
import com.wire.kalium.logic.data.id.TeamId
import com.wire.kalium.logic.data.id.toApi
import com.wire.kalium.logic.data.id.toDao
Expand Down Expand Up @@ -77,6 +78,7 @@ interface ConversationMapper {
fun toApiModel(name: String?, members: List<UserId>, teamId: String?, options: ConversationOptions): CreateConversationRequest

fun fromMigrationModel(conversation: Conversation): ConversationEntity
fun fromFailedGroupConversationToEntity(conversationId: NetworkQualifiedId): ConversationEntity
}

@Suppress("TooManyFunctions", "LongParameterList")
Expand Down Expand Up @@ -113,6 +115,7 @@ internal class ConversationMapperImpl(
receiptMode = receiptModeMapper.fromApiToDaoModel(apiModel.receiptMode),
messageTimer = apiModel.messageTimer,
userMessageTimer = null, // user picked self deletion timer is only persisted locally
hasIncompleteMetadata = false
)

override fun fromApiModelToDaoModel(apiModel: ConvProtocol): Protocol = when (apiModel) {
Expand Down Expand Up @@ -357,6 +360,31 @@ internal class ConversationMapperImpl(
)
}

/**
* Default values and marked as [ConversationEntity.hasIncompleteMetadata] = true.
* So later we can re-fetch them.
*/
override fun fromFailedGroupConversationToEntity(conversationId: NetworkQualifiedId): ConversationEntity = ConversationEntity(
id = conversationId.toDao(),
name = null,
type = ConversationEntity.Type.GROUP,
teamId = null,
protocolInfo = ProtocolInfo.Proteus,
mutedStatus = ConversationEntity.MutedStatus.ALL_ALLOWED,
mutedTime = 0,
removedBy = null,
creatorId = "",
lastNotificationDate = "1970-01-01T00:00:00.000Z".toInstant(),
lastModifiedDate = "1970-01-01T00:00:00.000Z".toInstant(),
lastReadDate = "1970-01-01T00:00:00.000Z".toInstant(),
access = emptyList(),
accessRole = emptyList(),
receiptMode = ConversationEntity.ReceiptMode.DISABLED,
messageTimer = null,
userMessageTimer = null,
hasIncompleteMetadata = true
)

private fun ConversationResponse.getProtocolInfo(mlsGroupState: GroupState?): ProtocolInfo {
return when (protocol) {
ConvProtocol.MLS -> ProtocolInfo.MLS(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.wire.kalium.logic.data.client.MLSClientProvider
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.GroupID
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.toApi
import com.wire.kalium.logic.data.id.toCrypto
Expand Down Expand Up @@ -210,6 +211,7 @@ interface ConversationRepository {
suspend fun getConversationUnreadEventsCount(conversationId: ConversationId): Either<StorageFailure, Long>
suspend fun getUserSelfDeletionTimer(conversationId: ConversationId): Either<StorageFailure, SelfDeletionTimer?>
suspend fun updateUserSelfDeletionTimer(conversationId: ConversationId, selfDeletionTimer: SelfDeletionTimer): Either<CoreFailure, Unit>
suspend fun syncConversationsWithoutMetadata(): Either<CoreFailure, Unit>
}

@Suppress("LongParameterList", "TooManyFunctions")
Expand Down Expand Up @@ -266,7 +268,8 @@ internal class ConversationDataSource internal constructor(
}.onSuccess { conversations ->
if (conversations.conversationsFailed.isNotEmpty()) {
kaliumLogger.withFeatureId(CONVERSATIONS)
.d("Skipping ${conversations.conversationsFailed.size} conversations failed")
.d("Handling ${conversations.conversationsFailed.size} conversations failed")
persistIncompleteConversations(conversations.conversationsFailed)
}
if (conversations.conversationsNotFound.isNotEmpty()) {
kaliumLogger.withFeatureId(CONVERSATIONS)
Expand Down Expand Up @@ -751,6 +754,31 @@ internal class ConversationDataSource internal constructor(
)
}

override suspend fun syncConversationsWithoutMetadata(): Either<CoreFailure, Unit> = wrapStorageRequest {
val conversationsWithoutMetadata = conversationDAO.getConversationsWithoutMetadata()
if (conversationsWithoutMetadata.isNotEmpty()) {
kaliumLogger.d("Numbers of conversations to refresh: ${conversationsWithoutMetadata.size}")
val conversationsWithoutMetadataIds = conversationsWithoutMetadata.map { it.toApi() }
wrapApiRequest {
conversationApi.fetchConversationsListDetails(conversationsWithoutMetadataIds)
}.onSuccess {
persistConversations(it.conversationsFound, null)
}
}
}

private suspend fun persistIncompleteConversations(
conversationsFailed: List<NetworkQualifiedId>
): Either<CoreFailure, Unit> {
return wrapStorageRequest {
if (conversationsFailed.isNotEmpty()) {
conversationDAO.insertConversations(conversationsFailed.map { conversationId ->
conversationMapper.fromFailedGroupConversationToEntity(conversationId)
})
}
}
}

companion object {
const val DEFAULT_MEMBER_ROLE = "wire_member"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,22 @@ internal class NewGroupConversationSystemMessagesCreatorImpl(
status = Message.Status.SENT,
visibility = Message.Visibility.VISIBLE
)
).also { createFailedToAddSystemMessage(conversationResponse) }
}
}

private suspend fun createFailedToAddSystemMessage(conversationResponse: ConversationResponse) {
if (conversationResponse.failedToAdd.isNotEmpty()) {
val messageStartedWithFailedMembers = Message.System(
uuid4().toString(),
MessageContent.MemberChange.FailedToAdd(conversationResponse.failedToAdd.map { it.toModel() }),
conversationResponse.id.toModel(),
DateTimeUtil.currentIsoDateTimeString(),
selfUserId,
Message.Status.SENT,
Message.Visibility.VISIBLE
)
persistMessage(messageStartedWithFailedMembers)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ package com.wire.kalium.logic.data.message

import com.wire.kalium.logger.obfuscateDomain
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.util.serialization.toJsonElement
import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.util.serialization.toJsonElement
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.serialization.encodeToString
Expand Down Expand Up @@ -80,7 +80,8 @@ sealed interface Message {
val editStatus: EditStatus,
val expirationData: ExpirationData? = null,
val reactions: Reactions = Reactions.EMPTY,
val expectsReadConfirmation: Boolean = false
val expectsReadConfirmation: Boolean = false,
val deliveryStatus: DeliveryStatus = DeliveryStatus.CompleteDelivery
) : Sendable, Standalone {

@Suppress("LongMethod")
Expand Down Expand Up @@ -133,7 +134,8 @@ sealed interface Message {
"visibility" to "$visibility",
"senderClientId" to senderClientId.value.obfuscateId(),
"editStatus" to editStatus.toLogMap(),
"expectsReadConfirmation" to "$expectsReadConfirmation"
"expectsReadConfirmation" to "$expectsReadConfirmation",
"deliveryStatus" to "$deliveryStatus"
)

properties.putAll(standardProperties)
Expand Down Expand Up @@ -456,3 +458,12 @@ enum class AssetType {

typealias ReactionsCount = Map<String, Int>
typealias UserReactions = Set<String>

sealed class DeliveryStatus {
data class PartialDelivery(
val recipientsFailedWithNoClients: List<UserId>,
val recipientsFailedDelivery: List<UserId>
) : DeliveryStatus()

object CompleteDelivery : DeliveryStatus()
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.wire.kalium.logic.data.notification.LocalNotificationMessageAuthor
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.di.MapperProvider
import com.wire.kalium.persistence.dao.message.AssetTypeEntity
import com.wire.kalium.persistence.dao.message.DeliveryStatusEntity
import com.wire.kalium.persistence.dao.message.MessageEntity
import com.wire.kalium.persistence.dao.message.MessageEntityContent
import com.wire.kalium.persistence.dao.message.MessagePreviewEntity
Expand Down Expand Up @@ -105,6 +106,7 @@ class MessageMapperImpl(
}
}

@Suppress("ComplexMethod")
override fun fromEntityToMessage(message: MessageEntity): Message.Standalone {
val status = when (message.status) {
MessageEntity.Status.PENDING -> Message.Status.PENDING
Expand Down Expand Up @@ -138,7 +140,14 @@ class MessageMapperImpl(
reactions = Message.Reactions(message.reactions.totalReactions, message.reactions.selfUserReactions),
senderUserName = message.senderName,
isSelfMessage = message.isSelfMessage,
expectsReadConfirmation = message.expectsReadConfirmation
expectsReadConfirmation = message.expectsReadConfirmation,
deliveryStatus = when (val recipientsFailure = message.deliveryStatus) {
is DeliveryStatusEntity.CompleteDelivery -> DeliveryStatus.CompleteDelivery
is DeliveryStatusEntity.PartialDelivery -> DeliveryStatus.PartialDelivery(
recipientsFailedWithNoClients = recipientsFailure.recipientsFailedWithNoClients.map { it.toModel() },
recipientsFailedDelivery = recipientsFailure.recipientsFailedDelivery.map { it.toModel() }
)
},
)

is MessageEntity.System -> Message.System(
Expand Down
Loading