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: propagate if a guest link is password protected #1985

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
450cd7e
CanCreatePasswordProtectedLinksUseCase
MohamadJaara Aug 3, 2023
6de5bf0
docs
MohamadJaara Aug 3, 2023
85e0067
detekt
MohamadJaara Aug 3, 2023
71f615c
test
MohamadJaara Aug 3, 2023
bea3b98
feat: handle code update and delete event
MohamadJaara Aug 5, 2023
291a3a1
test
MohamadJaara Aug 5, 2023
8319d74
code delete code
MohamadJaara Aug 5, 2023
2afac1a
Merge branch 'feat/create-password-protected-invite-code/epic' into f…
MohamadJaara Aug 5, 2023
104e4b1
inject handler
MohamadJaara Aug 5, 2023
7a652cc
detekt
MohamadJaara Aug 5, 2023
ed53d21
add fake value for is password protected
MohamadJaara Aug 5, 2023
b2c9208
add has password to event DTO
MohamadJaara Aug 5, 2023
3965904
missing any
MohamadJaara Aug 5, 2023
9edd777
fix test
MohamadJaara Aug 5, 2023
8188474
detekt
MohamadJaara Aug 5, 2023
3d3ce86
db migration
MohamadJaara Aug 5, 2023
3388fbe
typo in db migration
MohamadJaara Aug 5, 2023
40ea534
Update logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receive…
MohamadJaara Aug 7, 2023
97fe18f
Update logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receive…
MohamadJaara Aug 7, 2023
c5c3795
file name
MohamadJaara Aug 7, 2023
241f2bc
fix(tests): fix e2ei mock order timing
mchenani Aug 7, 2023
cd396fd
support api v4 for password protected invite links
MohamadJaara Aug 7, 2023
ecaaf4f
fix: unresolved reference
MohamadJaara Aug 7, 2023
b86f2f2
Merge remote-tracking branch 'origin/develop' into feat/create-passwo…
MohamadJaara Aug 11, 2023
a7bba68
fix merge issues
MohamadJaara Aug 11, 2023
f28ea43
Merge branch 'develop' into feat/create-password-protected-conv-invit…
MohamadJaara Aug 14, 2023
733718d
Merge branch 'feat/create-password-protected-invite-code/epic' into f…
MohamadJaara Aug 14, 2023
5136f63
Merge branch 'develop' into feat/create-password-protected-conv-invit…
MohamadJaara Aug 14, 2023
7c546e0
feat: generate password protected guest link
MohamadJaara Aug 14, 2023
7558feb
Merge branch 'develop' into feat/create-password-protected-conv-invit…
MohamadJaara Aug 14, 2023
65bd6d7
Merge branch 'feat/create-password-protected-invite-code/epic' into f…
MohamadJaara Aug 14, 2023
78413e9
add test
MohamadJaara Aug 14, 2023
066df79
detekt
MohamadJaara Aug 15, 2023
6433af7
fix test
MohamadJaara Aug 15, 2023
c57893b
feat: propagate if a guest link is password protected
MohamadJaara Aug 15, 2023
452dc58
Merge branch 'feat/create-password-protected-invite-code/epic' into f…
MohamadJaara Aug 15, 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
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
Loading