Skip to content

Commit

Permalink
feat: update supported protocols after deleting a client (#2083)
Browse files Browse the repository at this point in the history
  • Loading branch information
typfel committed Oct 13, 2023
1 parent fecd0b3 commit cad4139
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1388,7 +1388,9 @@ class UserSessionScope internal constructor(
userRepository,
authenticationScope.secondFactorVerificationRepository,
slowSyncRepository,
cachedClientIdClearer
cachedClientIdClearer,
users.updateSupportedProtocols,
oneOnOneResolver
)
val conversations: ConversationScope by lazy {
ConversationScope(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.feature.CachedClientIdClearer
import com.wire.kalium.logic.feature.CurrentClientIdProvider
import com.wire.kalium.logic.feature.ProteusClientProvider
import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolver
import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCase
import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCaseImpl
import com.wire.kalium.logic.feature.keypackage.RefillKeyPackagesUseCase
import com.wire.kalium.logic.feature.keypackage.RefillKeyPackagesUseCaseImpl
import com.wire.kalium.logic.feature.session.DeregisterTokenUseCase
import com.wire.kalium.logic.feature.session.DeregisterTokenUseCaseImpl
import com.wire.kalium.logic.feature.session.UpgradeCurrentSessionUseCase
import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase
import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCase
import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCaseImpl
import com.wire.kalium.util.DelicateKaliumApi
Expand All @@ -66,7 +68,9 @@ class ClientScope @OptIn(DelicateKaliumApi::class) internal constructor(
private val userRepository: UserRepository,
private val secondFactorVerificationRepository: SecondFactorVerificationRepository,
private val slowSyncRepository: SlowSyncRepository,
private val cachedClientIdClearer: CachedClientIdClearer
private val cachedClientIdClearer: CachedClientIdClearer,
private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase,
private val oneOnOneResolver: OneOnOneResolver
) {
@OptIn(DelicateKaliumApi::class)
val register: RegisterClientUseCase
Expand All @@ -85,7 +89,13 @@ class ClientScope @OptIn(DelicateKaliumApi::class) internal constructor(

val selfClients: FetchSelfClientsFromRemoteUseCase get() = FetchSelfClientsFromRemoteUseCaseImpl(clientRepository, clientIdProvider)
val observeClientDetailsUseCase: ObserveClientDetailsUseCase get() = ObserveClientDetailsUseCaseImpl(clientRepository, clientIdProvider)
val deleteClient: DeleteClientUseCase get() = DeleteClientUseCaseImpl(clientRepository)
val deleteClient: DeleteClientUseCase
get() = DeleteClientUseCaseImpl(
clientRepository,
updateSupportedProtocols,
userRepository,
oneOnOneResolver
)
val needsToRegisterClient: NeedsToRegisterClientUseCase
get() = NeedsToRegisterClientUseCaseImpl(clientIdProvider, sessionRepository, proteusClientProvider, selfUserId)
val deregisterNativePushToken: DeregisterTokenUseCase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.NetworkFailure
import com.wire.kalium.logic.data.client.ClientRepository
import com.wire.kalium.logic.data.client.DeleteClientParam
import com.wire.kalium.logic.data.user.UserRepository
import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolver
import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.functional.onSuccess
import com.wire.kalium.network.exceptions.KaliumException
import com.wire.kalium.network.exceptions.isBadRequest
import com.wire.kalium.network.exceptions.isInvalidCredentials
Expand All @@ -36,9 +41,24 @@ interface DeleteClientUseCase {
suspend operator fun invoke(param: DeleteClientParam): DeleteClientResult
}

class DeleteClientUseCaseImpl(private val clientRepository: ClientRepository) : DeleteClientUseCase {
internal class DeleteClientUseCaseImpl(
private val clientRepository: ClientRepository,
private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase,
private val userRepository: UserRepository,
private val oneOnOneResolver: OneOnOneResolver
) : DeleteClientUseCase {
override suspend operator fun invoke(param: DeleteClientParam): DeleteClientResult =
clientRepository.deleteClient(param).fold(
clientRepository.deleteClient(param)
.onSuccess {
updateSupportedProtocols().onSuccess { updated ->
if (updated) {
userRepository.fetchAllOtherUsers().flatMap {
oneOnOneResolver.resolveAllOneOnOneConversations()
}
}
}
}
.fold(
{
handleError(it)
}, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import kotlinx.datetime.Instant
* Updates the supported protocols of the current user.
*/
interface UpdateSupportedProtocolsUseCase {
suspend operator fun invoke(): Either<CoreFailure, Unit>
suspend operator fun invoke(): Either<CoreFailure, Boolean>
}

internal class UpdateSupportedProtocolsUseCaseImpl(
Expand All @@ -50,7 +50,7 @@ internal class UpdateSupportedProtocolsUseCaseImpl(
private val featureConfigRepository: FeatureConfigRepository
) : UpdateSupportedProtocolsUseCase {

override suspend operator fun invoke(): Either<CoreFailure, Unit> {
override suspend operator fun invoke(): Either<CoreFailure, Boolean> {
kaliumLogger.d("Updating supported protocols")

return (userRepository.getSelfUser()?.let { selfUser ->
Expand All @@ -59,16 +59,16 @@ internal class UpdateSupportedProtocolsUseCaseImpl(
"Updating supported protocols = $newSupportedProtocols previously = ${selfUser.supportedProtocols}"
)
if (newSupportedProtocols != selfUser.supportedProtocols) {
userRepository.updateSupportedProtocols(newSupportedProtocols)
userRepository.updateSupportedProtocols(newSupportedProtocols).map { true }
} else {
Either.Right(Unit)
Either.Right(false)
}
}.flatMapLeft {
if (it is NetworkFailure.FeatureNotSupported) {
kaliumLogger.w(
"Skip updating supported protocols since it's not supported by the backend API"
)
Either.Right(Unit)
Either.Right(false)
} else {
Either.Left(it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.functional.flatMap
import com.wire.kalium.logic.functional.isRight
import com.wire.kalium.logic.functional.map
import com.wire.kalium.logic.functional.nullableFold
import com.wire.kalium.logic.functional.onFailure
import com.wire.kalium.logic.kaliumLogger
Expand Down Expand Up @@ -82,7 +83,7 @@ internal class SlowSyncWorkerImpl(

performStep(SlowSyncStep.SELF_USER, syncSelfUser::invoke)
.continueWithStep(SlowSyncStep.FEATURE_FLAGS, syncFeatureConfigs::invoke)
.continueWithStep(SlowSyncStep.UPDATE_SUPPORTED_PROTOCOLS, updateSupportedProtocols::invoke)
.continueWithStep(SlowSyncStep.UPDATE_SUPPORTED_PROTOCOLS) { updateSupportedProtocols.invoke().map { } }
.continueWithStep(SlowSyncStep.CONVERSATIONS, syncConversations::invoke)
.continueWithStep(SlowSyncStep.CONNECTIONS, syncConnections::invoke)
.continueWithStep(SlowSyncStep.SELF_TEAM, syncSelfTeam::invoke)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@

package com.wire.kalium.logic.feature.client

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.NetworkFailure
import com.wire.kalium.logic.data.client.ClientRepository
import com.wire.kalium.logic.data.client.DeleteClientParam
import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase
import com.wire.kalium.logic.framework.TestClient
import com.wire.kalium.logic.functional.Either
import com.wire.kalium.logic.test_util.TestNetworkException
import com.wire.kalium.logic.util.arrangement.UserRepositoryArrangement
import com.wire.kalium.logic.util.arrangement.UserRepositoryArrangementImpl
import com.wire.kalium.logic.util.arrangement.mls.OneOnOneResolverArrangement
import com.wire.kalium.logic.util.arrangement.mls.OneOnOneResolverArrangementImpl
import com.wire.kalium.network.exceptions.KaliumException
import io.ktor.utils.io.errors.IOException
import io.mockative.Mock
Expand All @@ -35,46 +41,34 @@ import io.mockative.mock
import io.mockative.once
import io.mockative.verify
import kotlinx.coroutines.test.runTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertIs
import kotlin.test.assertSame

class DeleteClientUseCaseTest {

@Mock
private val clientRepository = mock(classOf<ClientRepository>())

private lateinit var deleteClient: DeleteClientUseCase

@BeforeTest
fun setup() {
deleteClient = DeleteClientUseCaseImpl(clientRepository)
}

@Test
fun givenDeleteClientParams_whenDeleting_thenTheRepositoryShouldBeCalledWithCorrectParameters() = runTest {
val params = DELETE_CLIENT_PARAMETERS
given(clientRepository)
.suspendFunction(clientRepository::deleteClient)
.whenInvokedWith(anything())
.then { Either.Left(TEST_FAILURE) }

val (arrangement, deleteClient) = arrange {
withDeleteClient(Either.Left(TEST_FAILURE))
}

deleteClient(params)

verify(clientRepository)
.suspendFunction(clientRepository::deleteClient)
verify(arrangement.clientRepository)
.suspendFunction(arrangement.clientRepository::deleteClient)
.with(eq(params))
.wasInvoked(once)
}

@Test
fun givenRepositoryDeleteClientFailsDueToGenericError_whenDeleting_thenGenericErrorShouldBeReturned() = runTest {
val genericFailure = TEST_FAILURE
given(clientRepository)
.suspendFunction(clientRepository::deleteClient)
.whenInvokedWith(anything())
.then { Either.Left(genericFailure) }
val (_, deleteClient) = arrange {
withDeleteClient(Either.Left(genericFailure))
}

val result = deleteClient(DELETE_CLIENT_PARAMETERS)

Expand All @@ -85,10 +79,9 @@ class DeleteClientUseCaseTest {
@Test
fun givenRepositoryDeleteClientFailsDueToWrongPassword_whenDeleting_thenInvalidCredentialsErrorShouldBeReturned() = runTest {
val wrongPasswordFailure = NetworkFailure.ServerMiscommunication(TestNetworkException.invalidCredentials)
given(clientRepository)
.suspendFunction(clientRepository::deleteClient)
.whenInvokedWith(anything())
.then { Either.Left(wrongPasswordFailure) }
val (_, deleteClient) = arrange {
withDeleteClient(Either.Left(wrongPasswordFailure))
}

val result = deleteClient(DELETE_CLIENT_PARAMETERS)

Expand All @@ -98,10 +91,9 @@ class DeleteClientUseCaseTest {
@Test
fun givenRepositoryDeleteClientFailsDueToMissingPassword_whenDeleting_thenPasswordAuthRequiredErrorShouldBeReturned() = runTest {
val missingPasswordFailure = NetworkFailure.ServerMiscommunication(TestNetworkException.missingAuth)
given(clientRepository)
.suspendFunction(clientRepository::deleteClient)
.whenInvokedWith(anything())
.then { Either.Left(missingPasswordFailure) }
val (_, deleteClient) = arrange {
withDeleteClient(Either.Left(missingPasswordFailure))
}

val result = deleteClient(DELETE_CLIENT_PARAMETERS)

Expand All @@ -111,17 +103,85 @@ class DeleteClientUseCaseTest {
@Test
fun givenRepositoryDeleteClientFailsDueToBadRequest_whenDeleting_thenInvalidCredentialsErrorShouldBeReturned() = runTest {
val badRequest = NetworkFailure.ServerMiscommunication(TestNetworkException.badRequest)
given(clientRepository)
.suspendFunction(clientRepository::deleteClient)
.whenInvokedWith(anything())
.then { Either.Left(badRequest) }
val (_, deleteClient) = arrange {
withDeleteClient(Either.Left(badRequest))
}

val result = deleteClient(DELETE_CLIENT_PARAMETERS)

assertIs<DeleteClientResult.Failure.InvalidCredentials>(result)
}

@Test
fun givenRepositoryDeleteClientSucceeds_whenDeleting_thenUpdateSupportedProtocols() = runTest {
val (arrangement, deleteClient) = arrange {
withDeleteClient(Either.Right(Unit))
withUpdateSupportedProtocols(Either.Right(false))
}

val result = deleteClient(DELETE_CLIENT_PARAMETERS)

assertIs<DeleteClientResult.Success>(result)
verify(arrangement.updateSupportedProtocols)
.suspendFunction(arrangement.updateSupportedProtocols::invoke)
.wasInvoked(exactly = once)
}

@Test
fun givenSupportedProtocolsAreUpdated_whenDeleting_thenResolveActiveOneOnOneConversations() = runTest {
val (arrangement, deleteClient) = arrange {
withDeleteClient(Either.Right(Unit))
withUpdateSupportedProtocols(Either.Right(true))
withFetchAllOtherUsersReturning(Either.Right(Unit))
withResolveAllOneOnOneConversationsReturning(Either.Right(Unit))
}

val result = deleteClient(DELETE_CLIENT_PARAMETERS)

assertIs<DeleteClientResult.Success>(result)
verify(arrangement.updateSupportedProtocols)
.suspendFunction(arrangement.updateSupportedProtocols::invoke)
.wasInvoked(exactly = once)
}

private class Arrangement(private val block: Arrangement.() -> Unit) :
UserRepositoryArrangement by UserRepositoryArrangementImpl(),
OneOnOneResolverArrangement by OneOnOneResolverArrangementImpl()
{
@Mock
val clientRepository = mock(classOf<ClientRepository>())

@Mock
val updateSupportedProtocols = mock(classOf<UpdateSupportedProtocolsUseCase>())

fun withDeleteClient(result: Either<NetworkFailure, Unit>) {
given(clientRepository)
.suspendFunction(clientRepository::deleteClient)
.whenInvokedWith(anything())
.then { result }
}

fun withUpdateSupportedProtocols(result: Either<CoreFailure, Boolean>) {
given(updateSupportedProtocols)
.suspendFunction(updateSupportedProtocols::invoke)
.whenInvoked()
.thenReturn(result)
}

fun arrange() = run {
block()
this@Arrangement to DeleteClientUseCaseImpl(
clientRepository = clientRepository,
updateSupportedProtocols = updateSupportedProtocols,
userRepository = userRepository,
oneOnOneResolver = oneOnOneResolver,
)
}
}

private companion object {
fun arrange(configuration: Arrangement.() -> Unit) = Arrangement(configuration).arrange()

val CLIENT = TestClient.CLIENT
val DELETE_CLIENT_PARAMETERS = DeleteClientParam("pass", CLIENT.id)
val TEST_FAILURE = NetworkFailure.ServerMiscommunication(KaliumException.GenericError(IOException("no internet")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ class SlowSyncWorkerTest {
given(updateSupportedProtocols)
.suspendFunction(updateSupportedProtocols::invoke)
.whenInvoked()
.thenReturn(success)
.thenReturn(Either.Right(true))
}

fun withUpdateSupportedProtocolsFailure() = apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ internal interface UserRepositoryArrangement {
fun withGetKnownUserReturning(result: Flow<OtherUser?>)

fun withGetUsersWithOneOnOneConversationReturning(result: List<OtherUser>)

fun withFetchAllOtherUsersReturning(result: Either<CoreFailure, Unit>)
}

internal class UserRepositoryArrangementImpl: UserRepositoryArrangement {
Expand Down Expand Up @@ -102,4 +104,12 @@ internal class UserRepositoryArrangementImpl: UserRepositoryArrangement {
.whenInvoked()
.thenReturn(result)
}

override fun withFetchAllOtherUsersReturning(result: Either<CoreFailure, Unit>) {
given(userRepository)
.suspendFunction(userRepository::fetchAllOtherUsers)
.whenInvoked()
.thenReturn(result)
}

}

0 comments on commit cad4139

Please sign in to comment.