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: persist and observe data about user being informed about legalhold change [WPB-4870] #2299

Merged
merged 8 commits into from
Dec 13, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ interface UserConfigRepository {

fun observeLegalHoldRequest(): Flow<Either<StorageFailure, LegalHoldRequest>>
suspend fun deleteLegalHoldRequest(): Either<StorageFailure, Unit>
suspend fun setLegalHoldChangeNotified(isNotified: Boolean): Either<StorageFailure, Unit>
suspend fun observeLegalHoldChangeNotified(): Flow<Either<StorageFailure, Boolean>>
}

@Suppress("TooManyFunctions")
Expand Down Expand Up @@ -418,4 +420,10 @@ internal class UserConfigDataSource internal constructor(
wrapStorageRequest {
userConfigDAO.clearLegalHoldRequest()
}

override suspend fun setLegalHoldChangeNotified(isNotified: Boolean): Either<StorageFailure, Unit> =
wrapStorageRequest { userConfigDAO.setLegalHoldChangeNotified(isNotified) }

override suspend fun observeLegalHoldChangeNotified(): Flow<Either<StorageFailure, Boolean>> =
userConfigDAO.observeLegalHoldChangeNotified().wrapStorageRequest()
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ internal class TeamDataSource(

override suspend fun approveLegalHoldRequest(teamId: TeamId, password: String?): Either<CoreFailure, Unit> = wrapApiRequest {
teamsApi.approveLegalHoldRequest(teamId.value, selfUserId.value, password)
}.flatMap {
legalHoldHandler.handleEnable(
eventMapper.legalHoldEnabled(
id = LocalId.generate(),
transient = true,
live = false,
eventContentDTO = EventContentDTO.User.LegalHoldEnabledDTO(id = selfUserId.toString())
)
)
}.onSuccess {
userConfigDAO.clearLegalHoldRequest()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ import com.wire.kalium.logic.feature.legalhold.ApproveLegalHoldRequestUseCase
import com.wire.kalium.logic.feature.legalhold.ApproveLegalHoldRequestUseCaseImpl
import com.wire.kalium.logic.feature.legalhold.FetchLegalHoldForSelfUserFromRemoteUseCase
import com.wire.kalium.logic.feature.legalhold.FetchLegalHoldForSelfUserFromRemoteUseCaseImpl
import com.wire.kalium.logic.feature.legalhold.MarkLegalHoldChangeAsNotifiedForSelfUseCase
import com.wire.kalium.logic.feature.legalhold.MarkLegalHoldChangeAsNotifiedForSelfUseCaseImpl
import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldChangeNotifiedForSelfUseCase
import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldChangeNotifiedForSelfUseCaseImpl
import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldForSelfUserUseCase
import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldForSelfUserUseCaseImpl
import com.wire.kalium.logic.feature.legalhold.ObserveLegalHoldRequestUseCase
Expand Down Expand Up @@ -1346,6 +1350,12 @@ class UserSessionScope internal constructor(
val observeLegalHoldForSelfUser: ObserveLegalHoldForSelfUserUseCase
get() = ObserveLegalHoldForSelfUserUseCaseImpl(userId, observeLegalHoldStateForUser)

val observeLegalHoldChangeNotifiedForSelf: ObserveLegalHoldChangeNotifiedForSelfUseCase
get() = ObserveLegalHoldChangeNotifiedForSelfUseCaseImpl(userConfigRepository, observeLegalHoldForSelfUser)

val markLegalHoldChangeAsNotifiedForSelf: MarkLegalHoldChangeAsNotifiedForSelfUseCase
get() = MarkLegalHoldChangeAsNotifiedForSelfUseCaseImpl(userConfigRepository)

val observeLegalHoldRequest: ObserveLegalHoldRequestUseCase
get() = ObserveLegalHoldRequestUseCaseImpl(
userConfigRepository = userConfigRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.legalhold

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.kaliumLogger

/**
* Use case that marks the recent legal hold change as already notified to the user.
*/
interface MarkLegalHoldChangeAsNotifiedForSelfUseCase {
suspend operator fun invoke(): Result

sealed class Result {
data object Success : Result()
data class Failure(val failure: CoreFailure) : Result()
}
}

internal class MarkLegalHoldChangeAsNotifiedForSelfUseCaseImpl internal constructor(
val userConfigRepository: UserConfigRepository,
) : MarkLegalHoldChangeAsNotifiedForSelfUseCase {
override suspend fun invoke(): MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result =
userConfigRepository.setLegalHoldChangeNotified(true).fold(
{ failure ->
kaliumLogger.i("Legal hold change notified failure: $failure")
MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result.Failure(failure)
},
{ MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result.Success }
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.legalhold

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.functional.fold
import com.wire.kalium.logic.kaliumLogger
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map

/**
* Use case that observes if the legal hold change should be notified to the user or if it has been already notified.
*/
interface ObserveLegalHoldChangeNotifiedForSelfUseCase {
suspend operator fun invoke(): Flow<Result>

sealed class Result {
data class ShouldNotify(val legalHoldState: LegalHoldState) : Result()
data object AlreadyNotified : Result()
data class Failure(val failure: CoreFailure) : Result()
}
}

internal class ObserveLegalHoldChangeNotifiedForSelfUseCaseImpl internal constructor(
val userConfigRepository: UserConfigRepository,
val observeLegalHoldForSelfUserUseCase: ObserveLegalHoldForSelfUserUseCase
) : ObserveLegalHoldChangeNotifiedForSelfUseCase {
@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun invoke(): Flow<ObserveLegalHoldChangeNotifiedForSelfUseCase.Result> =
userConfigRepository.observeLegalHoldChangeNotified()
.flatMapLatest {
it.fold(
{ failure ->
kaliumLogger.i("Legal hold change notified failure: $failure")
flowOf(ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.Failure(failure))
},
{ isNotified ->
if (isNotified)
flowOf(ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.AlreadyNotified)
else
observeLegalHoldForSelfUserUseCase()
saleniuk marked this conversation as resolved.
Show resolved Hide resolved
.map { legalHoldState ->
ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.ShouldNotify(legalHoldState)
}
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ internal class LegalHoldHandlerImpl internal constructor(
userConfigRepository.deleteLegalHoldRequest()
coroutineScope.launch {
fetchSelfClientsFromRemote()
userConfigRepository.setLegalHoldChangeNotified(false)
}
} else {
coroutineScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,11 @@ class TeamRepositoryTest {
}

@Test
fun givenTeamIdAndUserIdAndPassword_whenApprovingLegalHoldRequest_thenItShouldSucceedAndClearRequestLocally() = runTest {
fun givenTeamIdAndUserIdAndPassword_whenApprovingLegalHoldRequest_thenItShouldSucceedAndClearRequestLocallyAndCreateEvent() = runTest {
// given
val (arrangement, teamRepository) = Arrangement()
.withApiApproveLegalHoldSuccess()
.withHandleLegalHoldSuccesses()
.arrange()
// when
val result = teamRepository.approveLegalHoldRequest(teamId = TeamId(value = "teamId"), password = "password")
Expand All @@ -257,6 +258,10 @@ class TeamRepositoryTest {
verify(arrangement.userConfigDAO)
.suspendFunction(arrangement.userConfigDAO::clearLegalHoldRequest)
.wasInvoked(once)
verify(arrangement.legalHoldHandler)
.suspendFunction(arrangement.legalHoldHandler::handleEnable)
.with(matching { it.userId == TestUser.USER_ID })
.wasInvoked(once)
}

private fun testFetchingLegalHoldStatus(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.legalhold

import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.functional.Either
import io.mockative.Mock
import io.mockative.any
import io.mockative.given
import io.mockative.mock
import kotlinx.coroutines.test.runTest
import okio.IOException
import kotlin.test.Test
import kotlin.test.assertEquals

class MarkLegalHoldChangeAsNotifiedForSelfUseCaseTest {

@Test
fun givenASuccess_whenSettingLegalHoldChangeAsNotified_thenReturnSuccess() = runTest {
// given
val (_, useCase) = Arrangement()
.withSetLegalHoldChangeNotifiedResult(Either.Right(Unit))
.arrange()
// when
val result = useCase()
// then
assertEquals(MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result.Success, result)
}
@Test
fun givenAFailure_whenSettingLegalHoldChangeAsNotified_thenReturnSuccess() = runTest {
// given
val failure = StorageFailure.Generic(IOException())
val (_, useCase) = Arrangement()
.withSetLegalHoldChangeNotifiedResult(Either.Left(failure))
.arrange()
// when
val result = useCase()
// then
assertEquals(MarkLegalHoldChangeAsNotifiedForSelfUseCase.Result.Failure(failure), result)
}

private class Arrangement {
@Mock
val userConfigRepository = mock(UserConfigRepository::class)
val useCase: MarkLegalHoldChangeAsNotifiedForSelfUseCase = MarkLegalHoldChangeAsNotifiedForSelfUseCaseImpl(userConfigRepository)

fun arrange() = this to useCase
fun withSetLegalHoldChangeNotifiedResult(result: Either<StorageFailure, Unit>) = apply {
given(userConfigRepository)
.suspendFunction(userConfigRepository::setLegalHoldChangeNotified)
.whenInvokedWith(any())
.then { result }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.legalhold

import com.wire.kalium.logic.StorageFailure
import com.wire.kalium.logic.configuration.UserConfigRepository
import com.wire.kalium.logic.functional.Either
import io.mockative.Mock
import io.mockative.given
import io.mockative.mock
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import okio.IOException
import kotlin.test.Test
import kotlin.test.assertEquals

class ObserveLegalHoldChangeNotifiedForSelfUseCaseTest {

private fun testResult(
givenLegalHoldStateResult: LegalHoldState,
givenIsNotifiedResult: Either<StorageFailure, Boolean>,
expected: ObserveLegalHoldChangeNotifiedForSelfUseCase.Result,
) = runTest {
// given
val (_, useCase) = Arrangement()
.withLegalHoldChangeNotified(givenIsNotifiedResult)
.withLegalHoldEnabledState(givenLegalHoldStateResult)
.arrange()
// when
val result = useCase()
// then
assertEquals(expected, result.first())
}
@Test
fun givenStorageError_whenObserving_thenEmitFailure() =
StorageFailure.Generic(IOException()).let { failure ->
testResult(
givenLegalHoldStateResult = LegalHoldState.Enabled,
givenIsNotifiedResult = Either.Left(failure),
expected = ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.Failure(failure)
)
}
@Test
fun givenLegalHoldForSelfEnabledAndNotYetNotified_whenObserving_thenEmitShouldNotifyWithLegalHoldEnabledState() = testResult(
givenLegalHoldStateResult = LegalHoldState.Enabled,
givenIsNotifiedResult = Either.Right(false),
expected = ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.ShouldNotify(LegalHoldState.Enabled)
)
@Test
fun givenLegalHoldForSelfEnabledAndAlreadyNotified_whenObserving_thenEmitAlreadyNotified() = testResult(
givenLegalHoldStateResult = LegalHoldState.Enabled,
givenIsNotifiedResult = Either.Right(true),
expected = ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.AlreadyNotified
)
@Test
fun givenLegalHoldForSelfDisabledAndNotYetNotified_whenObserving_thenEmitShouldNotifyWithLegalHoldDisabledState() = testResult(
givenLegalHoldStateResult = LegalHoldState.Disabled,
givenIsNotifiedResult = Either.Right(false),
expected = ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.ShouldNotify(LegalHoldState.Disabled)
)
@Test
fun givenLegalHoldForSelfDisabledAndAlreadyNotified_whenObserving_thenEmitAlreadyNotified() = testResult(
givenLegalHoldStateResult = LegalHoldState.Disabled,
givenIsNotifiedResult = Either.Right(true),
expected = ObserveLegalHoldChangeNotifiedForSelfUseCase.Result.AlreadyNotified
)

private class Arrangement {
@Mock
val userConfigRepository = mock(UserConfigRepository::class)
@Mock
val observeLegalHoldForSelfUser = mock(ObserveLegalHoldForSelfUserUseCase::class)
val useCase: ObserveLegalHoldChangeNotifiedForSelfUseCase =
ObserveLegalHoldChangeNotifiedForSelfUseCaseImpl(userConfigRepository, observeLegalHoldForSelfUser)

fun arrange() = this to useCase
fun withLegalHoldEnabledState(result: LegalHoldState) = apply {
given(observeLegalHoldForSelfUser)
.suspendFunction(observeLegalHoldForSelfUser::invoke)
.whenInvoked()
.then { flowOf(result) }
}
fun withLegalHoldChangeNotified(result: Either<StorageFailure, Boolean>) = apply {
given(userConfigRepository)
.suspendFunction(userConfigRepository::observeLegalHoldChangeNotified)
.whenInvoked()
.then { flowOf(result) }
}
}
}
Loading
Loading