Skip to content

Commit

Permalink
feat: propagate if a guest link is password protected
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamadJaara committed Aug 15, 2023
1 parent 6433af7 commit c57893b
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import com.wire.kalium.logic.sync.receiver.conversation.ConversationMessageTimer
import com.wire.kalium.logic.sync.receiver.conversation.MemberJoinEventHandler
import com.wire.kalium.logic.sync.receiver.conversation.MemberLeaveEventHandler
import com.wire.kalium.logic.wrapApiRequest
import com.wire.kalium.logic.wrapNullableFlowStorageRequest
import com.wire.kalium.logic.wrapStorageRequest
import com.wire.kalium.network.api.base.authenticated.conversation.AddConversationMembersRequest
import com.wire.kalium.network.api.base.authenticated.conversation.AddServiceRequest
Expand All @@ -53,6 +54,7 @@ import com.wire.kalium.persistence.dao.conversation.ConversationDAO
import com.wire.kalium.persistence.dao.conversation.ConversationEntity
import com.wire.kalium.persistence.dao.message.LocalId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

interface ConversationGroupRepository {
suspend fun createGroupConversation(
Expand All @@ -78,7 +80,7 @@ interface ConversationGroupRepository {
): Either<NetworkFailure, EventContentDTO.Conversation.CodeUpdated>

suspend fun revokeGuestRoomLink(conversationId: ConversationId): Either<NetworkFailure, Unit>
suspend fun observeGuestRoomLink(conversationId: ConversationId): Flow<String?>
suspend fun observeGuestRoomLink(conversationId: ConversationId): Flow<Either<CoreFailure, ConversationGuestLink?>>
suspend fun updateMessageTimer(conversationId: ConversationId, messageTimer: Long?): Either<CoreFailure, Unit>
}

Expand Down Expand Up @@ -332,8 +334,11 @@ internal class ConversationGroupRepositoryImpl(
}
}

override suspend fun observeGuestRoomLink(conversationId: ConversationId): Flow<String?> =
conversationDAO.observeGuestRoomLinkByConversationId(conversationId.toDao())
override suspend fun observeGuestRoomLink(conversationId: ConversationId): Flow<Either<CoreFailure, ConversationGuestLink?>> =
wrapNullableFlowStorageRequest {
conversationDAO.observeGuestRoomLinkByConversationId(conversationId.toDao())
.map { it?.let { ConversationGuestLink(it.link, it.isPasswordProtected) } }
}

override suspend fun updateMessageTimer(
conversationId: ConversationId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.conversation

data class ConversationGuestLink(
val link: String,
val isPasswordProtected: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class UpdateConversationAccessRoleUseCase internal constructor(

syncManager.waitUntilLiveOrFailure().flatMap {
if (!accessRoles.contains(Conversation.AccessRole.GUEST)
&& !conversationGroupRepository.observeGuestRoomLink(conversationId).first().isNullOrEmpty()
&& conversationGroupRepository.observeGuestRoomLink(conversationId).first() != null
) {
conversationGroupRepository.revokeGuestRoomLink(conversationId)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,23 @@

package com.wire.kalium.logic.feature.conversation.guestroomlink

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.conversation.ConversationGroupRepository
import com.wire.kalium.logic.data.conversation.ConversationGuestLink
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.functional.Either
import kotlinx.coroutines.flow.Flow

/**
* observe guest room link
*/
interface ObserveGuestRoomLinkUseCase {
suspend operator fun invoke(conversationId: ConversationId): Flow<String?>
suspend operator fun invoke(conversationId: ConversationId): Flow<Either<CoreFailure, ConversationGuestLink?>>
}

class ObserveGuestRoomLinkUseCaseImpl internal constructor(
private val conversationGroupRepository: ConversationGroupRepository
) : ObserveGuestRoomLinkUseCase {
override suspend fun invoke(conversationId: ConversationId): Flow<String?> =
override suspend fun invoke(conversationId: ConversationId): Flow<Either<CoreFailure, ConversationGuestLink?>> =
conversationGroupRepository.observeGuestRoomLink(conversationId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import com.wire.kalium.network.exceptions.KaliumException
import com.wire.kalium.network.utils.NetworkResponse
import com.wire.kalium.persistence.dao.conversation.ConversationDAO
import com.wire.kalium.persistence.dao.conversation.ConversationEntity
import com.wire.kalium.persistence.dao.conversation.ConversationGuestLinkEntity
import com.wire.kalium.persistence.dao.conversation.ConversationViewEntity
import io.ktor.http.HttpStatusCode
import io.mockative.Mock
Expand All @@ -75,6 +76,7 @@ import io.mockative.matching
import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
Expand Down Expand Up @@ -694,8 +696,13 @@ class ConversationGroupRepositoryTest {
fun givenDaoRunsEmitsValues_whenObservingGuestRoomLink_thenPropagateGuestRoomLink() = runTest {
val conversationId = ConversationId("value", "domain")

val expected = ConversationGuestLinkEntity(
link = "link",
isPasswordProtected = false
)

val (arrangement, conversationGroupRepository) = Arrangement()
.withSuccessfulFetchOfGuestRoomLink()
.withSuccessfulFetchOfGuestRoomLink(flowOf(expected))
.arrange()

val result = conversationGroupRepository.observeGuestRoomLink(conversationId)
Expand All @@ -704,7 +711,10 @@ class ConversationGroupRepositoryTest {
.suspendFunction(arrangement.conversationDAO::observeGuestRoomLinkByConversationId)
.with(any())
.wasInvoked(exactly = once)
assertEquals(LINK, result.first())
result.first().shouldSucceed {
assertEquals(expected.link, it?.link)
assertEquals(expected.isPasswordProtected, it?.isPasswordProtected)
}
}

@Test
Expand Down Expand Up @@ -1131,11 +1141,13 @@ class ConversationGroupRepositoryTest {
)
}

fun withSuccessfulFetchOfGuestRoomLink() = apply {
fun withSuccessfulFetchOfGuestRoomLink(
result: Flow<ConversationGuestLinkEntity?>
) = apply {
given(conversationDAO)
.suspendFunction(conversationDAO::observeGuestRoomLinkByConversationId)
.whenInvokedWith(any())
.thenReturn(GUEST_ROOM_LINK_FLOW)
.thenReturn(result)
}

fun withSuccessfulNewConversationGroupStartedHandled() = apply {
Expand Down Expand Up @@ -1203,8 +1215,6 @@ class ConversationGroupRepositoryTest {

companion object {
private const val RAW_GROUP_ID = "mlsGroupId"
const val LINK = "www.wire.com"
private val GUEST_ROOM_LINK_FLOW = flowOf(LINK)
val GROUP_ID = GroupID(RAW_GROUP_ID)
val PROTEUS_PROTOCOL_INFO = ConversationEntity.ProtocolInfo.Proteus
val MLS_PROTOCOL_INFO = ConversationEntity.ProtocolInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.wire.kalium.logic.NetworkFailure
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.conversation.Conversation.ProtocolInfo
import com.wire.kalium.logic.data.conversation.ConversationGroupRepository
import com.wire.kalium.logic.data.conversation.ConversationGuestLink
import com.wire.kalium.logic.data.conversation.ConversationRepository
import com.wire.kalium.logic.data.conversation.MutedConversationStatus
import com.wire.kalium.logic.data.id.ConversationId
Expand All @@ -38,6 +39,7 @@ import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import okio.IOException
Expand Down Expand Up @@ -231,9 +233,11 @@ class UpdateConversationAccessUseCaseTest {
access = listOf(Conversation.Access.INVITE)
)

val expected = ConversationGuestLink("guestLink", false)

val (arrangement, updateConversationAccess) = Arrangement()
.withUpdateAccessInfoRetuning(Either.Right(Unit))
.withGuestRoomLink("link")
.withGuestRoomLink(flowOf(Either.Right(expected)))
.arrange()

updateConversationAccess(
Expand Down Expand Up @@ -315,9 +319,10 @@ class UpdateConversationAccessUseCaseTest {
fun givenError_whenCallingUpdateAccessInfo_thenFailureIsPropagated() = runTest {
val conversation = conversationStub

val expected: ConversationGuestLink? = null
val (arrangement, updateConversationAccess) = Arrangement()
.withUpdateAccessInfoRetuning(Either.Left(NetworkFailure.NoNetworkConnection(IOException())))
.withGuestRoomLink(null)
.withGuestRoomLink(flowOf(Either.Right(expected)))
.arrange()

// allowGuest = false, allowServices = true, allowNonTeamMember = true
Expand All @@ -338,9 +343,11 @@ class UpdateConversationAccessUseCaseTest {
fun givenAnGuestAccessRole_whenInvokingUpdateAccessInfo_thenRevokeGuestLinkShouldNotBeInvoked() = runTest {
val accessRoles = setOf(Conversation.AccessRole.GUEST)

val expected = ConversationGuestLink("guestLink", false)

val (arrangement, updateConversationAccessRole) = Arrangement()
.withWaitUntilLiveOrFailure(Either.Right(Unit))
.withGuestRoomLink("guestLink")
.withGuestRoomLink(flowOf(Either.Right(expected)))
.withUpdateAccessInfoRetuning(Either.Right(Unit))
.arrange()

Expand All @@ -358,9 +365,11 @@ class UpdateConversationAccessUseCaseTest {
fun givenAnAccessRoleWithoutGuestAndSyncFailing_whenInvokingUpdateAccessInfo_thenRevokeGuestLinkShouldNotBeInvoked() = runTest {
val accessRoles = setOf(Conversation.AccessRole.TEAM_MEMBER)

val expected = ConversationGuestLink("guestLink", false)

val (arrangement, updateConversationAccessRole) = Arrangement()
.withWaitUntilLiveOrFailure(Either.Left(CoreFailure.Unknown(RuntimeException("Error"))))
.withGuestRoomLink("guestLink")
.withGuestRoomLink(flowOf(Either.Right(expected)))
.withUpdateAccessInfoRetuning(Either.Right(Unit))
.arrange()

Expand All @@ -371,7 +380,6 @@ class UpdateConversationAccessUseCaseTest {
.suspendFunction(arrangement.conversationGroupRepository::revokeGuestRoomLink)
.with(any())
.wasNotInvoked()

}

companion object {
Expand Down Expand Up @@ -444,11 +452,11 @@ class UpdateConversationAccessUseCaseTest {
.thenReturn(either)
}

fun withGuestRoomLink(link: String?) = apply {
fun withGuestRoomLink(result: Flow<Either<CoreFailure, ConversationGuestLink?>>) = apply {
given(conversationGroupRepository)
.suspendFunction(conversationGroupRepository::observeGuestRoomLink)
.whenInvokedWith(any())
.thenReturn(flowOf(link))
.thenReturn(result)
}

fun arrange() = this to updateConversationAccess
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
package com.wire.kalium.logic.feature.conversation.guestroomlink

import com.wire.kalium.logic.data.conversation.ConversationGroupRepository
import com.wire.kalium.logic.data.conversation.ConversationGuestLink
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.util.shouldSucceed
import io.mockative.Mock
import io.mockative.classOf
import io.mockative.eq
Expand Down Expand Up @@ -46,24 +49,22 @@ class ObserveGuestRoomLinkUseCaseTest {
observeGuestRoomLink = ObserveGuestRoomLinkUseCaseImpl(conversationGroupRepository)
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun givenRepositoryEmitsValues_whenObservingGuestRoomLink_thenPropagateTheLink() = runTest {
val guestLink = "www.wire.com"
val guestLink = ConversationGuestLink("link", false)
given(conversationGroupRepository)
.suspendFunction(conversationGroupRepository::observeGuestRoomLink)
.whenInvokedWith(eq(conversationId))
.thenReturn(flowOf(guestLink))
.thenReturn(flowOf(Either.Right(guestLink)))

val result = observeGuestRoomLink(conversationId)
observeGuestRoomLink(conversationId).first().shouldSucceed {
assertEquals(guestLink, it)
}

assertEquals(guestLink, result.first())
verify(conversationGroupRepository)
.suspendFunction(conversationGroupRepository::observeGuestRoomLink)
.with(eq(conversationId))
.wasInvoked(exactly = once)


}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ SET guest_room_link = :link, is_guest_password_protected = :isPasswordProtected
WHERE qualified_id = :conversationId;

getGuestRoomLinkByConversationId:
SELECT guest_room_link FROM Conversation WHERE qualified_id = ?;
SELECT guest_room_link, is_guest_password_protected FROM Conversation WHERE qualified_id = :conversationId;

updateMessageTimer:
UPDATE Conversation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ interface ConversationDAO {
isPasswordProtected: Boolean
)

suspend fun observeGuestRoomLinkByConversationId(conversationId: QualifiedIDEntity): Flow<String?>
suspend fun observeGuestRoomLinkByConversationId(conversationId: QualifiedIDEntity): Flow<ConversationGuestLinkEntity?>
suspend fun updateMessageTimer(conversationId: QualifiedIDEntity, messageTimer: Long?)
suspend fun updateUserMessageTimer(conversationId: QualifiedIDEntity, messageTimer: Long?)
suspend fun getConversationsWithoutMetadata(): List<QualifiedIDEntity>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,9 @@ internal class ConversationDAOImpl internal constructor(
conversationQueries.updateGuestRoomLink(link, isPasswordProtected, conversationId)
}

override suspend fun observeGuestRoomLinkByConversationId(conversationId: QualifiedIDEntity): Flow<String?> =
conversationQueries.getGuestRoomLinkByConversationId(conversationId).asFlow().map {
it.executeAsOne().guest_room_link
override suspend fun observeGuestRoomLinkByConversationId(conversationId: QualifiedIDEntity): Flow<ConversationGuestLinkEntity?> =
conversationQueries.getGuestRoomLinkByConversationId(conversationId).asFlow().mapToOneOrNull().map {
it?.guest_room_link?.let { link -> ConversationGuestLinkEntity(link, it.is_guest_password_protected) }
}.flowOn(coroutineContext)

override suspend fun updateMessageTimer(conversationId: QualifiedIDEntity, messageTimer: Long?) = withContext(coroutineContext) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.persistence.dao.conversation

data class ConversationGuestLinkEntity(
val link: String,
val isPasswordProtected: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.wire.kalium.persistence.dao.asset.AssetDAO
import com.wire.kalium.persistence.dao.asset.AssetEntity
import com.wire.kalium.persistence.dao.conversation.ConversationDAO
import com.wire.kalium.persistence.dao.conversation.ConversationEntity
import com.wire.kalium.persistence.dao.conversation.ConversationGuestLinkEntity
import com.wire.kalium.persistence.dao.conversation.ConversationViewEntity
import com.wire.kalium.persistence.dao.conversation.MLS_DEFAULT_LAST_KEY_MATERIAL_UPDATE_MILLI
import com.wire.kalium.persistence.dao.conversation.ProposalTimerEntity
Expand Down Expand Up @@ -858,6 +859,40 @@ class ConversationDAOTest : BaseDatabaseTest() {
}
}

@Test
fun givenConversionWithNoCode_whenObservingCode_thenShouldReturnNull() = runTest {
conversationDAO.insertConversation(conversationEntity1)

conversationDAO.observeGuestRoomLinkByConversationId(conversationEntity1.id).test {
assertNull(awaitItem())
}
}

@Test
fun givenConversionWithCode_whenObservingCode_thenShouldReturnValue() = runTest {
conversationDAO.insertConversation(conversationEntity1)
conversationDAO.updateGuestRoomLink(conversationEntity1.id,"link", true)

conversationDAO.observeGuestRoomLinkByConversationId(conversationEntity1.id).test {
assertEquals(ConversationGuestLinkEntity("link", true), awaitItem())
}
}

@Test
fun givenConversionCodeRevoked_whenObservingCode_thenShouldReturnNull() = runTest {
conversationDAO.insertConversation(conversationEntity1)
conversationDAO.updateGuestRoomLink(conversationEntity1.id,"link", true)

conversationDAO.observeGuestRoomLinkByConversationId(conversationEntity1.id).test {
assertEquals(ConversationGuestLinkEntity("link", true), awaitItem())
}

conversationDAO.updateGuestRoomLink(conversationEntity1.id,null, true)
conversationDAO.observeGuestRoomLinkByConversationId(conversationEntity1.id).test {
assertNull(awaitItem())
}
}

private suspend fun insertTeamUserAndMember(team: TeamEntity, user: UserEntity, conversationId: QualifiedIDEntity) {
teamDAO.insertTeam(team)
userDAO.insertUser(user)
Expand Down

0 comments on commit c57893b

Please sign in to comment.