Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -14,9 +14,13 @@ import com.simprints.infra.config.store.models.PrivacyNoticeResult.FailedBecause
import com.simprints.infra.config.store.models.PrivacyNoticeResult.InProgress
import com.simprints.infra.config.store.models.PrivacyNoticeResult.Succeed
import com.simprints.infra.config.sync.ConfigManager
import com.simprints.infra.logging.Simber
import com.simprints.infra.network.ConnectivityTracker
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand Down Expand Up @@ -44,18 +48,31 @@ internal class PrivacyNoticeViewModel @Inject constructor(
}

fun retrievePrivacyNotice() = viewModelScope.launch {
val deviceConfiguration = configManager.getDeviceConfiguration()
configManager
.getPrivacyNotice(
authStore.signedInProjectId,
deviceConfiguration.language,
).map { it.toPrivacyNoticeViewState() }
.catch {
it.printStackTrace()
PrivacyNoticeState.ConsentNotAvailable
}.collect { _viewState.postValue(it) }
val projectId = authStore.signedInProjectId
val deviceLanguage = configManager.getDeviceConfiguration().language
val defaultLanguage = configManager.getProjectConfiguration().general.defaultLanguage

attemptDownloadingLanguage(projectId, deviceLanguage)
.flatMapLatest {
if (it is PrivacyNoticeState.ConsentNotAvailable) {
Simber.i("Privacy notice in ($deviceLanguage) not available")
attemptDownloadingLanguage(projectId, defaultLanguage)
} else {
flowOf(it)
}
}.catch {
Simber.i("Notice download failed", it)
emit(PrivacyNoticeState.ConsentNotAvailable)
}.collect {
_viewState.postValue(it)
}
}

private fun attemptDownloadingLanguage(
projectId: String,
deviceLanguage: String,
): Flow<PrivacyNoticeState> = configManager.getPrivacyNotice(projectId, deviceLanguage).map { it.toPrivacyNoticeViewState() }

private fun PrivacyNoticeResult.toPrivacyNoticeViewState(): PrivacyNoticeState = when (this) {
is Succeed -> PrivacyNoticeState.ConsentAvailable(consent)
is InProgress -> PrivacyNoticeState.DownloadInProgress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import org.junit.Test
internal class PrivacyNoticeViewModelTest {
companion object {
private const val PROJECT_ID = "projectId"
private const val LANGUAGE = "en"
private const val DEVICE_LANGUAGE = "en"
private const val DEFAULT_LANGUAGE = "fr"
}

@get:Rule
Expand All @@ -46,7 +47,8 @@ internal class PrivacyNoticeViewModelTest {
fun setUp() {
MockKAnnotations.init(this, relaxed = true)

coEvery { configManager.getDeviceConfiguration() } returns DeviceConfiguration(LANGUAGE, listOf(), "")
coEvery { configManager.getDeviceConfiguration() } returns DeviceConfiguration(DEVICE_LANGUAGE, listOf(), "")
coEvery { configManager.getProjectConfiguration().general.defaultLanguage } returns DEFAULT_LANGUAGE
every { authStore.signedInProjectId } returns PROJECT_ID

privacyNoticeViewModel = PrivacyNoticeViewModel(
Expand All @@ -58,8 +60,8 @@ internal class PrivacyNoticeViewModelTest {

@Test
fun `retrievePrivacyNotice should return DownloadInProgress when trying download`() = runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(LANGUAGE),
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEVICE_LANGUAGE),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
Expand All @@ -71,9 +73,9 @@ internal class PrivacyNoticeViewModelTest {

@Test
fun `retrievePrivacyNotice should return ContentAvailable when success received`() = runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(LANGUAGE),
PrivacyNoticeResult.Succeed(LANGUAGE, "some long consent"),
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEVICE_LANGUAGE),
PrivacyNoticeResult.Succeed(DEVICE_LANGUAGE, "some long consent"),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
Expand All @@ -84,24 +86,28 @@ internal class PrivacyNoticeViewModelTest {
}

@Test
fun `retrievePrivacyNotice should return ConsentNotAvailable when Failed received`() = runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(LANGUAGE),
PrivacyNoticeResult.Failed(LANGUAGE, Throwable()),
fun `retrievePrivacyNotice should attempt default language when Failed received with initial`() = runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEVICE_LANGUAGE),
PrivacyNoticeResult.Failed(DEVICE_LANGUAGE, Throwable()),
)
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEFAULT_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEFAULT_LANGUAGE),
PrivacyNoticeResult.Succeed(DEFAULT_LANGUAGE, "some long consent"),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
privacyNoticeViewModel.retrievePrivacyNotice()

val value = privacyNoticeLiveData.getOrAwaitValue()
Truth.assertThat(value).isInstanceOf(PrivacyNoticeState.ConsentNotAvailable::class.java)
Truth.assertThat(value).isInstanceOf(PrivacyNoticeState.ConsentAvailable::class.java)
}

@Test
fun `retrievePrivacyNotice should return BackendMaintenance when FailedBecauseBackendMaintenance received`() = runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(LANGUAGE),
PrivacyNoticeResult.FailedBecauseBackendMaintenance(LANGUAGE, Throwable()),
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEVICE_LANGUAGE),
PrivacyNoticeResult.FailedBecauseBackendMaintenance(DEVICE_LANGUAGE, Throwable()),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
Expand All @@ -111,11 +117,30 @@ internal class PrivacyNoticeViewModelTest {
Truth.assertThat(value).isInstanceOf(PrivacyNoticeState.BackendMaintenance::class.java)
}

@Test
fun `retrievePrivacyNotice should return BackendMaintenance when FailedBecauseBackendMaintenance receivedon default language`() =
runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEVICE_LANGUAGE),
PrivacyNoticeResult.Failed(DEVICE_LANGUAGE, Throwable()),
)
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEFAULT_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEFAULT_LANGUAGE),
PrivacyNoticeResult.FailedBecauseBackendMaintenance(DEVICE_LANGUAGE, Throwable()),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
privacyNoticeViewModel.retrievePrivacyNotice()

val value = privacyNoticeLiveData.getOrAwaitValue()
Truth.assertThat(value).isInstanceOf(PrivacyNoticeState.BackendMaintenance::class.java)
}

@Test
fun `retrievePrivacyNotice should return BackendMaintenance with estimation when FailedBecauseBackendMaintenance received`() = runTest {
coEvery { configManager.getPrivacyNotice(PROJECT_ID, LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(LANGUAGE),
PrivacyNoticeResult.FailedBecauseBackendMaintenance(LANGUAGE, Throwable(), 1000L),
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(DEVICE_LANGUAGE),
PrivacyNoticeResult.FailedBecauseBackendMaintenance(DEVICE_LANGUAGE, Throwable(), 1000L),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
Expand All @@ -129,7 +154,11 @@ internal class PrivacyNoticeViewModelTest {
@Test
fun `downloadPressed should retrieve notice when online`() = runTest {
every { connectivityTracker.isConnected() } returns true
coEvery { configManager.getPrivacyNotice(PROJECT_ID, LANGUAGE) } returns flowOf(PrivacyNoticeResult.InProgress(LANGUAGE))
coEvery { configManager.getPrivacyNotice(PROJECT_ID, DEVICE_LANGUAGE) } returns flowOf(
PrivacyNoticeResult.InProgress(
DEVICE_LANGUAGE,
),
)

val privacyNoticeLiveData = privacyNoticeViewModel.viewState
val showOfflineLiveData = privacyNoticeViewModel.showOffline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ interface ConfigRepository {

suspend fun clearData()

suspend fun getPrivacyNotice(
fun getPrivacyNotice(
projectId: String,
language: String,
): Flow<PrivacyNoticeResult>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ internal class ConfigRepositoryImpl @Inject constructor(
localDataSource.deletePrivacyNotices()
}

override suspend fun getPrivacyNotice(
override fun getPrivacyNotice(
projectId: String,
language: String,
): Flow<PrivacyNoticeResult> = flow {
Expand All @@ -102,14 +102,14 @@ internal class ConfigRepositoryImpl @Inject constructor(
is TokenizableString.Raw -> tokenizationProcessor.encrypt(
decrypted = moduleId,
tokenKeyType = TokenKeyType.ModuleId,
project = project
project = project,
)

is TokenizableString.Tokenized -> moduleId
}
}
)
)
},
),
),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ class ConfigManager @Inject constructor(
}
}

fun watchProjectConfiguration(): Flow<ProjectConfiguration> =
configRepository.watchProjectConfiguration()
.onStart { getProjectConfiguration() } // to invoke download if empty
fun watchProjectConfiguration(): Flow<ProjectConfiguration> = configRepository
.watchProjectConfiguration()
.onStart { getProjectConfiguration() } // to invoke download if empty

suspend fun getDeviceConfiguration(): DeviceConfiguration = configRepository.getDeviceConfiguration()

suspend fun updateDeviceConfiguration(update: suspend (t: DeviceConfiguration) -> DeviceConfiguration) =
configRepository.updateDeviceConfiguration(update)

suspend fun getPrivacyNotice(
fun getPrivacyNotice(
projectId: String,
language: String,
): Flow<PrivacyNoticeResult> = configRepository.getPrivacyNotice(
Expand Down