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: add useSFTForOneToOneCalls flag (WPB-7153) #2889

Merged
merged 4 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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 @@ -90,7 +90,8 @@ data class MLSMigrationModel(
)

data class ConferenceCallingModel(
val status: Status
val status: Status,
val useSFTForOneOnOneCalls: Boolean
)

data class E2EIModel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ interface UserConfigRepository {
suspend fun getSupportedProtocols(): Either<StorageFailure, Set<SupportedProtocol>>
fun setConferenceCallingEnabled(enabled: Boolean): Either<StorageFailure, Unit>
fun isConferenceCallingEnabled(): Either<StorageFailure, Boolean>
fun setUseSFTForOneOnOneCalls(shouldUse: Boolean): Either<StorageFailure, Unit>
fun shouldUseSFTForOneOnOneCalls(): Either<StorageFailure, Boolean>
fun setSecondFactorPasswordChallengeStatus(isRequired: Boolean): Either<StorageFailure, Unit>
fun isSecondFactorPasswordChallengeRequired(): Either<StorageFailure, Boolean>
fun isReadReceiptsEnabled(): Flow<Either<StorageFailure, Boolean>>
Expand Down Expand Up @@ -292,6 +294,14 @@ internal class UserConfigDataSource internal constructor(
userConfigStorage.isConferenceCallingEnabled()
}

override fun setUseSFTForOneOnOneCalls(shouldUse: Boolean): Either<StorageFailure, Unit> = wrapStorageRequest {
userConfigStorage.persistUseSftForOneOnOneCalls(shouldUse)
}

override fun shouldUseSFTForOneOnOneCalls(): Either<StorageFailure, Boolean> = wrapStorageRequest {
userConfigStorage.shouldUseSftForOneOnOneCalls()
}

override fun setSecondFactorPasswordChallengeStatus(isRequired: Boolean): Either<StorageFailure, Unit> =
wrapStorageRequest {
userConfigStorage.persistSecondFactorPasswordChallengeStatus(isRequired)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ class FeatureConfigMapperImpl : FeatureConfigMapper {

override fun fromDTO(data: FeatureConfigData.ConferenceCalling): ConferenceCallingModel =
ConferenceCallingModel(
status = fromDTO(data.status)
status = fromDTO(data.status),
useSFTForOneOnOneCalls = data.config.useSFTForOneToOneCalls
)

override fun fromDTO(data: FeatureConfigData.E2EI?): E2EIModel =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ internal class SyncFeatureConfigsUseCaseImpl(
conferenceCallingConfigHandler.handle(it.conferenceCallingModel)
passwordChallengeConfigHandler.handle(it.secondFactorPasswordChallengeModel)
selfDeletingMessagesConfigHandler.handle(it.selfDeletingMessagesModel)
it.e2EIModel?.let { e2EIModel -> e2EIConfigHandler.handle(e2EIModel) }
it.e2EIModel.let { e2EIModel -> e2EIConfigHandler.handle(e2EIModel) }
appLockConfigHandler.handle(it.appLockModel)
Either.Right(Unit)
}.onFailure { networkFailure ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.featureConfig.ConferenceCallingModel
import com.wire.kalium.logic.data.featureConfig.Status
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap

class ConferenceCallingConfigHandler(
private val userConfigRepository: UserConfigRepository
) {
fun handle(conferenceCallingConfig: ConferenceCallingModel): Either<CoreFailure, Unit> {
val conferenceCallingEnabled = conferenceCallingConfig.status == Status.ENABLED
return userConfigRepository.setConferenceCallingEnabled(conferenceCallingEnabled)
val result = userConfigRepository.setConferenceCallingEnabled(conferenceCallingEnabled).flatMap {
userConfigRepository.setUseSFTForOneOnOneCalls(conferenceCallingConfig.useSFTForOneOnOneCalls)
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.wire.kalium.logic.data.featureConfig.FeatureConfigMapperImpl
import com.wire.kalium.logic.data.featureConfig.Status
import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO
import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO
import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig
import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData
import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigResponse
import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureFlagStatusDTO
Expand Down Expand Up @@ -141,7 +142,7 @@ class FeatureConfigMapperTest {
ClassifiedDomainsConfigDTO(listOf("wire.com")),
FeatureFlagStatusDTO.ENABLED
),
FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)),
FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.wire.kalium.logic.util.shouldFail
import com.wire.kalium.logic.util.shouldSucceed
import com.wire.kalium.network.api.authenticated.featureConfigs.AppLockConfigDTO
import com.wire.kalium.network.api.authenticated.featureConfigs.ClassifiedDomainsConfigDTO
import com.wire.kalium.network.api.authenticated.featureConfigs.ConferenceCallingConfig
import com.wire.kalium.network.api.authenticated.featureConfigs.E2EIConfigDTO
import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigApi
import com.wire.kalium.network.api.authenticated.featureConfigs.FeatureConfigData
Expand Down Expand Up @@ -59,7 +60,7 @@ class FeatureConfigRepositoryTest {
ClassifiedDomainsConfigModel(listOf()),
Status.ENABLED
),
ConferenceCallingModel(Status.ENABLED),
ConferenceCallingModel(Status.ENABLED, false),
ConfigsStatusModel(Status.ENABLED),
ConfigsStatusModel(Status.ENABLED),
ConfigsStatusModel(Status.ENABLED),
Expand Down Expand Up @@ -145,7 +146,7 @@ class FeatureConfigRepositoryTest {
AppLockConfigDTO(true, 0), FeatureFlagStatusDTO.ENABLED
),
FeatureConfigData.ClassifiedDomains(ClassifiedDomainsConfigDTO(listOf()), FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED, ConferenceCallingConfig(false)),
FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED),
FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object FeatureConfigTest {
ClassifiedDomainsConfigModel(listOf()),
Status.ENABLED
),
conferenceCallingModel: ConferenceCallingModel = ConferenceCallingModel(Status.ENABLED),
conferenceCallingModel: ConferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false),
conversationGuestLinksModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED),
digitalSignaturesModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED),
fileSharingModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class SyncFeatureConfigsUseCaseTest {
fun givenConferenceCallingIsEnabled_whenSyncing_thenItShouldBeStoredAsEnabled() = runTest {
val (arrangement, syncFeatureConfigsUseCase) = Arrangement()
.withRemoteFeatureConfigsSucceeding(
FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.ENABLED))
FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false))
)
.withGetTeamSettingsSelfDeletionStatusSuccessful()
.withGetSupportedProtocolsReturning(null)
Expand All @@ -136,7 +136,7 @@ class SyncFeatureConfigsUseCaseTest {
fun givenConferenceCallingIsDisasbled_whenSyncing_thenItShouldBeStoredAsDisabled() = runTest {
val (arrangement, syncFeatureConfigsUseCase) = Arrangement()
.withRemoteFeatureConfigsSucceeding(
FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.DISABLED))
FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.DISABLED, false))
)
.withGetTeamSettingsSelfDeletionStatusSuccessful()
.withGetSupportedProtocolsReturning(null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Wire
* Copyright (C) 2024 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.featureConfig.handler

import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.data.featureConfig.ConferenceCallingModel
import com.wire.kalium.logic.data.featureConfig.Status
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.isLeft
import com.wire.kalium.logic.functional.isRight
import io.mockative.Mock
import io.mockative.any
import io.mockative.every
import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlin.test.Test
import kotlin.test.assertTrue

class ConferenceCallingConfigHandlerTest {

@Test
fun givenUserConfigRepositoryFailureForConferenceCallingEnabled_whenHandlingTheEvent_ThenReturnFailure() {
val conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false)
val (arrangement, conferenceCallingConfigHandler) = Arrangement()
.withSetConferenceCallingEnabledFailure()
.arrange()

val result = conferenceCallingConfigHandler.handle(conferenceCallingModel)

verify {
arrangement.userConfigRepository.setConferenceCallingEnabled(conferenceCallingModel.status.toBoolean())
}.wasInvoked(exactly = once)

verify {
arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(any())
}.wasNotInvoked()

assertTrue { result.isLeft() }
}

@Test
fun givenUserConfigRepositoryFailureForUseSFTForOneOnOneCalls_whenHandlingTheEvent_ThenReturnFailure() {
val conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false)
val (arrangement, conferenceCallingConfigHandler) = Arrangement()
.withSetConferenceCallingEnabledSuccess()
.withSetUseSFTForOneOnOneCallsFailure()
.arrange()

val result = conferenceCallingConfigHandler.handle(conferenceCallingModel)

verify {
arrangement.userConfigRepository.setConferenceCallingEnabled(conferenceCallingModel.status.toBoolean())
}.wasInvoked(exactly = once)

verify {
arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(any())
}.wasInvoked(exactly = once)

assertTrue { result.isLeft() }
}

@Test
fun givenUserConfigRepositorySuccess_whenHandlingTheEvent_ThenReturnUnit() {
val conferenceCallingModel = ConferenceCallingModel(Status.ENABLED, false)
val (arrangement, conferenceCallingConfigHandler) = Arrangement()
.withSetConferenceCallingEnabledSuccess()
.withSetUseSFTForOneOnOneCallsSuccess()
.arrange()

val result = conferenceCallingConfigHandler.handle(conferenceCallingModel)

verify {
arrangement.userConfigRepository.setConferenceCallingEnabled(conferenceCallingModel.status.toBoolean())
}.wasInvoked(exactly = once)

verify {
arrangement.userConfigRepository.setUseSFTForOneOnOneCalls(any())
}.wasInvoked(exactly = once)

assertTrue { result.isRight() }
}

private class Arrangement {

@Mock
val userConfigRepository: UserConfigRepository = mock(UserConfigRepository::class)

fun arrange() = run {
this@Arrangement to ConferenceCallingConfigHandler(
userConfigRepository = userConfigRepository
)
}

init {
every {
userConfigRepository.setAppLockStatus(any(), any(), any())
}.returns(Either.Right(Unit))
}

fun withSetConferenceCallingEnabledFailure() = apply {
every {
userConfigRepository.setConferenceCallingEnabled(any())
}.returns(Either.Left(StorageFailure.DataNotFound))
}

fun withSetConferenceCallingEnabledSuccess() = apply {
every {
userConfigRepository.setConferenceCallingEnabled(any())
}.returns(Either.Right(Unit))
}

fun withSetUseSFTForOneOnOneCallsFailure() = apply {
every {
userConfigRepository.setUseSFTForOneOnOneCalls(any())
}.returns(Either.Left(StorageFailure.DataNotFound))
}

fun withSetUseSFTForOneOnOneCallsSuccess() = apply {
every {
userConfigRepository.setUseSFTForOneOnOneCalls(any())
}.returns(Either.Right(Unit))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class FeatureConfigEventReceiverTest {
.arrange()

featureConfigEventReceiver.onEvent(
arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.ENABLED)),
arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.ENABLED, false)),
TestEvent.liveDeliveryInfo
)

Expand All @@ -134,7 +134,7 @@ class FeatureConfigEventReceiverTest {
.arrange()

featureConfigEventReceiver.onEvent(
event = arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.DISABLED)),
event = arrangement.newConferenceCallingUpdatedEvent(ConferenceCallingModel(Status.DISABLED, false)),
deliveryInfo = TestEvent.liveDeliveryInfo
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ enum class FeatureFlagStatusDTO {
DISABLED;
}

@Serializable
data class ConferenceCallingConfig(
@SerialName("useSFTForOneToOneCalls")
val useSFTForOneToOneCalls: Boolean
)

@Serializable
data class AppLockConfigDTO(
@SerialName("enforceAppLock")
Expand Down Expand Up @@ -155,7 +161,9 @@ sealed class FeatureConfigData {
@Serializable
data class ConferenceCalling(
@SerialName("status")
val status: FeatureFlagStatusDTO
val status: FeatureFlagStatusDTO,
@SerialName("config")
val config: ConferenceCallingConfig
) : FeatureConfigData()

@SerialName("conversationGuestLinks")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ interface UserConfigStorage {
*/
fun isConferenceCallingEnabled(): Boolean

fun persistUseSftForOneOnOneCalls(shouldUse: Boolean)

fun shouldUseSftForOneOnOneCalls(): Boolean

/**
* Get the saved flag to know whether user's Read Receipts are currently enabled or not
*/
Expand Down Expand Up @@ -503,6 +507,16 @@ class UserConfigStorageImpl(
DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE
)

override fun persistUseSftForOneOnOneCalls(shouldUse: Boolean) {
kaliumPreferences.putBoolean(USE_SFT_FOR_ONE_ON_ONE_CALLS, shouldUse)
}

override fun shouldUseSftForOneOnOneCalls(): Boolean =
kaliumPreferences.getBoolean(
USE_SFT_FOR_ONE_ON_ONE_CALLS,
DEFAULT_USE_SFT_FOR_ONE_ON_ONE_CALLS_VALUE
)

override fun areReadReceiptsEnabled(): Flow<Boolean> = areReadReceiptsEnabledFlow
.map { kaliumPreferences.getBoolean(ENABLE_READ_RECEIPTS, true) }
.onStart { emit(kaliumPreferences.getBoolean(ENABLE_READ_RECEIPTS, true)) }
Expand Down Expand Up @@ -577,8 +591,10 @@ class UserConfigStorageImpl(
const val E2EI_SETTINGS = "end_to_end_identity_settings"
const val E2EI_NOTIFICATION_TIME = "end_to_end_identity_notification_time"
const val ENABLE_CONFERENCE_CALLING = "enable_conference_calling"
const val USE_SFT_FOR_ONE_ON_ONE_CALLS = "use_sft_for_one_on_one_calls"
const val ENABLE_READ_RECEIPTS = "enable_read_receipts"
const val DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE = false
const val DEFAULT_USE_SFT_FOR_ONE_ON_ONE_CALLS_VALUE = false
const val REQUIRE_SECOND_FACTOR_PASSWORD_CHALLENGE =
"require_second_factor_password_challenge"
const val ENABLE_SCREENSHOT_CENSORING = "enable_screenshot_censoring"
Expand Down
Loading