Skip to content

Commit

Permalink
Merge branch 'develop' into fix/handle-federation-not-enabled-cherry-…
Browse files Browse the repository at this point in the history
…pick
  • Loading branch information
Garzas authored Nov 10, 2023
2 parents 3b4cd67 + eb47fb6 commit 0d0ee68
Show file tree
Hide file tree
Showing 16 changed files with 491 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class MessageRepositoryExtensionsTest {
fun givenParameters_whenPaginatedMessagesByConversation_thenShouldCallDaoExtensionsWithRightParameters() = runTest {
val pagingConfig = PagingConfig(20)
val pager = Pager(pagingConfig) { fakePagingSource }
val startingOffset = 0

val kaliumPager = KaliumPager(pager, fakePagingSource, StandardTestDispatcher())
val (arrangement, messageRepositoryExtensions) = Arrangement()
Expand All @@ -65,7 +66,8 @@ class MessageRepositoryExtensionsTest {
messageRepositoryExtensions.getPaginatedMessagesByConversationIdAndVisibility(
TestConversation.ID,
visibilities,
pagingConfig
pagingConfig,
startingOffset
)

verify(arrangement.messageDaoExtensions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ actual interface MessageRepositoryExtensions {
conversationId: ConversationId,
visibility: List<Message.Visibility>,
pagingConfig: PagingConfig,
startingOffset: Int
): Flow<PagingData<Message.Standalone>>
}

Expand All @@ -46,11 +47,13 @@ actual class MessageRepositoryExtensionsImpl actual constructor(
conversationId: ConversationId,
visibility: List<Message.Visibility>,
pagingConfig: PagingConfig,
startingOffset: Int
): Flow<PagingData<Message.Standalone>> {
val pager: KaliumPager<MessageEntity> = messageDAO.platformExtensions.getPagerForConversation(
conversationId.toDao(),
visibility.map { it.toEntityVisibility() },
pagingConfig
pagingConfig,
startingOffset
)

return pager.pagingDataFlow.map { pagingData: PagingData<MessageEntity> ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ class GetPaginatedFlowOfMessagesByConversationUseCase internal constructor(
suspend operator fun invoke(
conversationId: ConversationId,
visibility: List<Message.Visibility> = Message.Visibility.values().toList(),
startingOffset: Int,
pagingConfig: PagingConfig
): Flow<PagingData<Message.Standalone>> = messageRepository.extensions.getPaginatedMessagesByConversationIdAndVisibility(
conversationId, visibility, pagingConfig
conversationId, visibility, pagingConfig, startingOffset
).flowOn(dispatcher.io)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ 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.SelfTeamIdProvider
import com.wire.kalium.logic.data.id.TeamId
import com.wire.kalium.logic.data.id.toApi
import com.wire.kalium.logic.data.id.toCrypto
import com.wire.kalium.logic.data.id.toDao
import com.wire.kalium.logic.data.id.toModel
import com.wire.kalium.logic.data.message.MessageMapper
import com.wire.kalium.logic.data.message.SelfDeletionTimer
import com.wire.kalium.logic.data.message.UnreadEventType
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.di.MapperProvider
import com.wire.kalium.logic.data.id.SelfTeamIdProvider
import com.wire.kalium.logic.data.message.SelfDeletionTimer
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.fold
Expand Down Expand Up @@ -526,6 +526,7 @@ internal class ConversationDataSource internal constructor(
conversationDAO.getConversationIds(type.toDAO(), protocol.toDao(), teamId?.value)
.map { it.toModel() }
}

override suspend fun getTeamConversationIdsReadyToCompleteMigration(teamId: TeamId): Either<StorageFailure, List<QualifiedID>> =
wrapStorageRequest {
conversationDAO.getTeamConversationIdsReadyToCompleteMigration(teamId.value)
Expand All @@ -539,7 +540,16 @@ internal class ConversationDataSource internal constructor(
conversationDAO.observeGetConversationByQualifiedID(conversationID.toDao())
.wrapStorageRequest()
// TODO we don't need last message and unread count here, we should discuss to divide model for list and for details
.mapRight { conversationMapper.fromDaoModelToDetails(it, null, mapOf()) }
.map { eitherConversationView ->
eitherConversationView.flatMap {
try {
Either.Right(conversationMapper.fromDaoModelToDetails(it, null, mapOf()))
} catch (error: IllegalArgumentException) {
kaliumLogger.e("require field in conversation Details", error)
Either.Left(StorageFailure.DataNotFound)
}
}
}
.distinctUntilChanged()

override suspend fun fetchConversation(conversationID: ConversationId): Either<CoreFailure, Unit> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ interface MessageRepository {
conversationId: ConversationId
): Either<CoreFailure, List<Message.Standalone>>

suspend fun getSearchedConversationMessagePosition(
conversationId: ConversationId,
messageId: String
): Either<StorageFailure, Int>

val extensions: MessageRepositoryExtensions
}

Expand Down Expand Up @@ -649,4 +654,14 @@ class MessageDataSource(
conversationId = conversationId.toDao()
).map(messageMapper::fromEntityToMessage)
}

override suspend fun getSearchedConversationMessagePosition(
conversationId: ConversationId,
messageId: String
): Either<StorageFailure, Int> = wrapStorageRequest {
messageDAO.getSearchedConversationMessagePosition(
conversationId = conversationId.toDao(),
messageId = messageId
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.feature.message

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.logic.functional.fold

/**
* Gets the Selected Message Position from Search
*
* @param conversationId [ConversationId] the id of the conversation where the search is happening.
* @param messageId [String] the id of the selected message from search.
*
* @result [Result] Success with Int position. Failure with StorageFailure.
*/
interface GetSearchedConversationMessagePositionUseCase {

suspend operator fun invoke(
conversationId: ConversationId,
messageId: String
): Result

sealed interface Result {
data class Success(val position: Int) : Result
data class Failure(val cause: CoreFailure) : Result
}
}

internal class GetSearchedConversationMessagePositionUseCaseImpl internal constructor(
private val messageRepository: MessageRepository
) : GetSearchedConversationMessagePositionUseCase {

override suspend fun invoke(
conversationId: ConversationId,
messageId: String
): GetSearchedConversationMessagePositionUseCase.Result = messageRepository
.getSearchedConversationMessagePosition(
conversationId = conversationId,
messageId = messageId
).fold(
{ GetSearchedConversationMessagePositionUseCase.Result.Failure(it) },
{ GetSearchedConversationMessagePositionUseCase.Result.Success(it) }
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -328,4 +328,9 @@ class MessageScope internal constructor(
get() = GetConversationMessagesFromSearchQueryUseCaseImpl(
messageRepository = messageRepository
)

val getSearchedConversationMessagePosition: GetSearchedConversationMessagePositionUseCase
get() = GetSearchedConversationMessagePositionUseCaseImpl(
messageRepository = messageRepository
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,38 @@ class MessageRepositoryTest {
)
}

@Test
fun givenSearchedMessages_whenMessageIsSelected_thenReturnMessagePosition() = runTest {
// given
val qualifiedIdEntity = TEST_QUALIFIED_ID_ENTITY
val conversationId = TEST_CONVERSATION_ID
val message = TEST_MESSAGE_ENTITY.copy(
id = "msg1",
conversationId = qualifiedIdEntity,
content = MessageEntityContent.Text("message 1")
)
val expectedMessagePosition = 113
val (_, messageRepository) = Arrangement()
.withSelectedMessagePosition(
conversationId = conversationId.toDao(),
messageId = message.id,
result = expectedMessagePosition
)
.arrange()

// when
val result = messageRepository.getSearchedConversationMessagePosition(
conversationId = conversationId,
messageId = message.id
)

// then
assertEquals(
expectedMessagePosition,
(result as Either.Right).value
)
}

private class Arrangement {

@Mock
Expand Down Expand Up @@ -673,6 +705,17 @@ class MessageRepositoryTest {
.thenReturn(messages)
}

fun withSelectedMessagePosition(
conversationId: QualifiedIDEntity,
messageId: String,
result: Int
) = apply {
given(messageDAO)
.suspendFunction(messageDAO::getSearchedConversationMessagePosition)
.whenInvokedWith(eq(conversationId), eq(messageId))
.thenReturn(result)
}

fun arrange() = this to MessageDataSource(
messageApi = messageApi,
mlsMessageApi = mlsMessageApi,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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.feature.message.message

import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.message.MessageRepository
import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCase
import com.wire.kalium.logic.feature.message.GetSearchedConversationMessagePositionUseCaseImpl
import com.wire.kalium.logic.framework.TestConversation
import com.wire.kalium.logic.framework.TestMessage
import com.wire.kalium.logic.functional.Either
import io.mockative.Mock
import io.mockative.given
import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs

class GetSearchedConversationMessagePositionUseCaseTest {

@Test
fun givenConversationIdAndMessageId_whenInvokingUseCase_thenShouldCallMessageRepository() = runTest {
val (arrangement, getSearchedConversationMessagePosition) = Arrangement()
.withRepositoryMessagePositionReturning(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID,
response = Either.Left(StorageFailure.DataNotFound)
)
.arrange()

getSearchedConversationMessagePosition(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID
)

verify(arrangement.messageRepository)
.coroutine {
arrangement.messageRepository.getSearchedConversationMessagePosition(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID
)
}
.wasInvoked(exactly = once)
}

@Test
fun givenRepositoryFails_whenInvokingUseCase_thenShouldPropagateTheFailure() = runTest {
val cause = StorageFailure.DataNotFound
val (_, getSearchedConversationMessagePosition) = Arrangement()
.withRepositoryMessagePositionReturning(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID,
response = Either.Left(StorageFailure.DataNotFound)
)
.arrange()

val result = getSearchedConversationMessagePosition(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID
)

assertIs<GetSearchedConversationMessagePositionUseCase.Result.Failure>(result)
assertEquals(cause, result.cause)
}

@Test
fun givenRepositorySucceeds_whenInvokingUseCase_thenShouldPropagateTheSuccess() = runTest {
val expectedMessagePosition = 113
val (_, getSearchedConversationMessagePosition) = Arrangement()
.withRepositoryMessagePositionReturning(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID,
response = Either.Right(expectedMessagePosition)
)
.arrange()

val result = getSearchedConversationMessagePosition(
conversationId = CONVERSATION_ID,
messageId = MESSAGE_ID
)

assertIs<GetSearchedConversationMessagePositionUseCase.Result.Success>(result)
assertEquals(expectedMessagePosition, result.position)
}

private inner class Arrangement {

@Mock
val messageRepository: MessageRepository = mock(MessageRepository::class)

private val getSearchedConversationMessagePosition by lazy {
GetSearchedConversationMessagePositionUseCaseImpl(messageRepository = messageRepository)
}

suspend fun withRepositoryMessagePositionReturning(
conversationId: ConversationId,
messageId: String,
response: Either<StorageFailure, Int>
) = apply {
given(messageRepository)
.coroutine {
messageRepository.getSearchedConversationMessagePosition(
conversationId = conversationId,
messageId = messageId
)
}
.thenReturn(response)
}

fun arrange() = this to getSearchedConversationMessagePosition
}

private companion object {
const val MESSAGE_ID = TestMessage.TEST_MESSAGE_ID
val MESSAGE = TestMessage.TEXT_MESSAGE
val CONVERSATION_ID = TestConversation.ID
}
}
Loading

0 comments on commit 0d0ee68

Please sign in to comment.