Added EventHandler for User & Addresses.

This commit is contained in:
Neil Marietta 2021-04-30 10:26:13 +02:00
parent 65a6e83a4f
commit e6e1912330
5 changed files with 130 additions and 80 deletions

View File

@ -20,8 +20,8 @@ package ch.protonmail.android.activities;
import static ch.protonmail.android.settings.pin.ValidatePinActivityKt.EXTRA_FRAGMENT_TITLE;
import static ch.protonmail.android.settings.pin.ValidatePinActivityKt.EXTRA_PIN_VALID;
import static ch.protonmail.android.worker.FetchUserInfoWorkerKt.FETCH_USER_INFO_WORKER_NAME;
import static ch.protonmail.android.worker.FetchUserInfoWorkerKt.FETCH_USER_INFO_WORKER_RESULT;
import static ch.protonmail.android.worker.FetchUserWorkerKt.FETCH_USER_INFO_WORKER_NAME;
import static ch.protonmail.android.worker.FetchUserWorkerKt.FETCH_USER_INFO_WORKER_RESULT;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
@ -38,13 +38,10 @@ import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.work.WorkManager;
import com.birbit.android.jobqueue.JobManager;
@ -77,7 +74,7 @@ import ch.protonmail.android.utils.AppUtil;
import ch.protonmail.android.utils.CustomLocale;
import ch.protonmail.android.utils.INetworkConfiguratorCallback;
import ch.protonmail.android.worker.FetchMailSettingsWorker;
import ch.protonmail.android.worker.FetchUserInfoWorker;
import ch.protonmail.android.worker.FetchUserWorker;
import dagger.hilt.android.AndroidEntryPoint;
import timber.log.Timber;
@ -118,7 +115,7 @@ public abstract class BaseActivity extends AppCompatActivity implements INetwork
@Inject
protected WorkManager workManager;
@Inject
protected FetchUserInfoWorker.Enqueuer fetchUserInfoWorkerEnqueuer;
protected FetchUserWorker.Enqueuer fetchUserInfoWorkerEnqueuer;
@Inject
protected FetchMailSettingsWorker.Enqueuer fetchMailSettingsWorkerEnqueuer;
@ -401,6 +398,7 @@ public abstract class BaseActivity extends AppCompatActivity implements INetwork
});
btnClose.setOnClickListener(v -> finish());
btnCheckAgain.setOnClickListener(v -> {
// TODO: Remove fetchUserInfoWorkerEnqueuer, not adapted for this usage.
fetchUserInfoWorkerEnqueuer.invoke(user.getId());
workManager.getWorkInfosForUniqueWorkLiveData(FETCH_USER_INFO_WORKER_NAME)
.observe(this, workInfo -> {

View File

@ -59,6 +59,8 @@ import ch.protonmail.android.utils.AppUtil
import ch.protonmail.android.utils.MessageUtils
import ch.protonmail.android.worker.FetchContactsDataWorker
import ch.protonmail.android.worker.FetchContactsEmailsWorker
import ch.protonmail.android.worker.FetchUserAddressesWorker
import ch.protonmail.android.worker.FetchUserWorker
import com.google.gson.JsonSyntaxException
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
@ -72,6 +74,8 @@ class EventHandler @AssistedInject constructor(
private val messageDetailsRepositoryFactory: MessageDetailsRepository.AssistedFactory,
private val fetchContactEmails: FetchContactsEmailsWorker.Enqueuer,
private val fetchContactsData: FetchContactsDataWorker.Enqueuer,
private val fetchUserWorkerEnqueuer: FetchUserWorker.Enqueuer,
private val fetchUserAddressesWorkerEnqueuer: FetchUserAddressesWorker.Enqueuer,
private val databaseProvider: DatabaseProvider,
private val launchInitialDataFetch: LaunchInitialDataFetch,
@Assisted val userId: Id
@ -240,12 +244,12 @@ class EventHandler @AssistedInject constructor(
writeMailSettings(context, mailSettings)
}
if (user != null) {
// TODO: Refresh User.
AppUtil.postEventOnUi(RefreshDrawerEvent())
// Core is the source of truth. Workaround: Force refresh Core.
fetchUserWorkerEnqueuer(userId)
}
if (addresses != null) {
// TODO: Refresh Addresses.
AppUtil.postEventOnUi(RefreshDrawerEvent())
// Core is the source of truth. Workaround: Force refresh Core.
fetchUserAddressesWorkerEnqueuer(userId)
}
if (counts != null) {
writeUnreadUpdates(counts)

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2020 Proton Technologies AG
*
* This file is part of ProtonMail.
*
* ProtonMail 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.
*
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
*/
package ch.protonmail.android.worker
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkRequest
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import ch.protonmail.android.attachments.KEY_INPUT_DATA_USER_ID_STRING
import ch.protonmail.android.domain.entity.Id
import me.proton.core.domain.entity.UserId
import me.proton.core.user.domain.UserManager
import me.proton.core.util.kotlin.takeIfNotBlank
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import javax.inject.Inject
// region constants
const val FETCH_ADDRESSES_WORKER_NAME = "FetchUserAddressesWorker"
// endregion
@HiltWorker
class FetchUserAddressesWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val userManager: UserManager,
private val oldUserManager: ch.protonmail.android.core.UserManager
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val userIdString = inputData.getString(KEY_INPUT_DATA_USER_ID_STRING)?.takeIfNotBlank()
?: return Result.failure(workDataOf(KEY_WORKER_ERROR_DESCRIPTION to "Cannot proceed with empty user id"))
return runCatching {
userManager.getAddresses(UserId(userIdString), refresh = true).also {
oldUserManager.clearCache()
}
}.fold(
onSuccess = { Result.success() },
onFailure = { Result.failure() }
)
}
class Enqueuer @Inject constructor(private val workManager: WorkManager) {
operator fun invoke(userId: Id): WorkRequest {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val data = Data.Builder()
.putString(KEY_INPUT_DATA_USER_ID_STRING, userId.s)
.build()
val request = OneTimeWorkRequestBuilder<FetchUserAddressesWorker>()
.setConstraints(constraints)
.setInputData(data)
.build()
workManager.enqueueUniqueWork(FETCH_ADDRESSES_WORKER_NAME, ExistingWorkPolicy.KEEP, request)
return request
}
}
}

View File

@ -33,29 +33,23 @@ import androidx.work.WorkerParameters
import androidx.work.workDataOf
import ch.protonmail.android.attachments.KEY_INPUT_DATA_USER_ID_STRING
import ch.protonmail.android.domain.entity.Id
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import me.proton.core.domain.entity.UserId
import me.proton.core.user.domain.UserManager
import me.proton.core.user.domain.entity.Delinquent
import me.proton.core.util.kotlin.takeIfNotBlank
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import javax.inject.Inject
/**
* A `Worker` that handles fetching user info.
*
* @author Stefanija Boshkovska
*/
// region constants
const val FETCH_USER_INFO_WORKER_NAME = "FetchUserInfoWorker"
const val FETCH_USER_INFO_WORKER_RESULT = "FetchUserInfoWorkerResult"
const val FETCH_USER_INFO_WORKER_EXCEPTION_MESSAGE = "FetchUserInfoWorkerExceptionMessage"
// endregion
@Deprecated("Remove this worker, its not needed and not adapted for the current usage.")
@Deprecated("Refactor resultData + BaseActivity.checkDelinquency")
@HiltWorker
class FetchUserInfoWorker @AssistedInject constructor(
class FetchUserWorker @AssistedInject constructor(
@Assisted context: Context,
@Assisted params: WorkerParameters,
private val userManager: UserManager,
@ -71,12 +65,11 @@ class FetchUserInfoWorker @AssistedInject constructor(
oldUserManager.clearCache()
}
}.map { user ->
// TODO: Remove this result.
workDataOf(FETCH_USER_INFO_WORKER_RESULT to (user.delinquent != Delinquent.None))
}.fold(
onSuccess = { resultData -> Result.success(resultData) },
onFailure = { exception ->
Result.failure(workDataOf(FETCH_USER_INFO_WORKER_EXCEPTION_MESSAGE to exception.message))
}
onFailure = { Result.failure() }
)
}
@ -91,7 +84,7 @@ class FetchUserInfoWorker @AssistedInject constructor(
.putString(KEY_INPUT_DATA_USER_ID_STRING, userId.s)
.build()
val request = OneTimeWorkRequestBuilder<FetchUserInfoWorker>()
val request = OneTimeWorkRequestBuilder<FetchUserWorker>()
.setConstraints(constraints)
.setInputData(data)
.build()

View File

@ -24,37 +24,28 @@ import android.text.TextUtils
import androidx.work.ListenableWorker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import ch.protonmail.android.api.ProtonMailApiManager
import ch.protonmail.android.api.models.User
import ch.protonmail.android.api.models.UserInfo
import ch.protonmail.android.api.models.address.AddressesResponse
import ch.protonmail.android.api.segments.RESPONSE_CODE_UNAUTHORIZED
import ch.protonmail.android.core.Constants
import ch.protonmail.android.core.UserManager
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.RelaxedMockK
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs
import io.mockk.spyk
import kotlinx.coroutines.runBlocking
import me.proton.core.test.kotlin.TestDispatcherProvider
import java.io.IOException
import me.proton.core.network.domain.ApiException
import me.proton.core.user.domain.UserManager
import me.proton.core.user.domain.entity.Delinquent
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
/**
* Tests the functionality of [FetchUserInfoWorker].
* Tests the functionality of [FetchUserWorker].
*
* @author Stefanija Boshkovska
*/
class FetchUserInfoWorkerTest {
class FetchUserWorkerTest {
@RelaxedMockK
private lateinit var context: Context
@ -62,13 +53,13 @@ class FetchUserInfoWorkerTest {
@RelaxedMockK
private lateinit var parameters: WorkerParameters
@MockK
private lateinit var protonMailApiManager: ProtonMailApiManager
@MockK
private lateinit var userManager: UserManager
private lateinit var worker: FetchUserInfoWorker
@MockK
private lateinit var oldUserManager: ch.protonmail.android.core.UserManager
private lateinit var worker: FetchUserWorker
@BeforeTest
fun setUp() {
@ -77,36 +68,23 @@ class FetchUserInfoWorkerTest {
mockkStatic(TextUtils::class)
every { TextUtils.isEmpty(any()) } returns false
worker = FetchUserInfoWorker(
worker = FetchUserWorker(
context,
parameters,
protonMailApiManager,
userManager,
TestDispatcherProvider
oldUserManager
)
}
@Test
fun `should return new user delinquency data when both API calls are successful`() {
runBlocking {
val mockUser = spyk<User> {
every { delinquent } returns false
val mockUser = spyk<me.proton.core.user.domain.entity.User> {
every { delinquent } returns Delinquent.None
every { name } returns "name"
every { setAddresses(any()) } just runs
}
val mockUserInfoResponse = mockk<UserInfo> {
every { code } returns Constants.RESPONSE_CODE_OK
every { user } returns mockUser
}
val mockAddressesResponse = mockk<AddressesResponse> {
every { code } returns Constants.RESPONSE_CODE_OK
every { addresses } returns mockk()
}
coEvery { protonMailApiManager.fetchUserInfo() } returns mockUserInfoResponse
coEvery { protonMailApiManager.fetchAddresses() } returns mockAddressesResponse
every { userManager.currentLegacyUser } returns mockUser
coEvery { userManager.getUser(any(), any()) } returns mockUser
val expectedResult = ListenableWorker.Result.success(workDataOf(FETCH_USER_INFO_WORKER_RESULT to false))
@ -121,20 +99,11 @@ class FetchUserInfoWorkerTest {
@Test
fun `should return old user delinquency data when at least one of the API calls responds with error code`() {
runBlocking {
val mockUser = spyk<User> {
every { delinquent } returns true
}
val mockUserInfoResponse = mockk<UserInfo> {
every { code } returns Constants.RESPONSE_CODE_OK
}
val mockAddressesResponse = mockk<AddressesResponse> {
every { code } returns RESPONSE_CODE_UNAUTHORIZED
val mockUser = spyk<me.proton.core.user.domain.entity.User> {
every { delinquent } returns Delinquent.InvoiceDelinquent
}
coEvery { protonMailApiManager.fetchUserInfo() } returns mockUserInfoResponse
coEvery { protonMailApiManager.fetchAddresses() } returns mockAddressesResponse
every { userManager.currentLegacyUser } returns mockUser
coEvery { userManager.getUser(any(), any()) } returns mockUser
val expectedResult = ListenableWorker.Result.success(workDataOf(FETCH_USER_INFO_WORKER_RESULT to true))
@ -149,17 +118,14 @@ class FetchUserInfoWorkerTest {
@Test
fun `should return failure result when at least one of the API calls throws an exception`() {
runBlocking {
val mockUserInfoResponse = mockk<UserInfo> {
every { code } returns Constants.RESPONSE_CODE_OK
}
val mockException = spyk<IOException> {
val mockException = spyk<ApiException> {
every { message } returns "exception"
}
coEvery { protonMailApiManager.fetchUserInfo() } returns mockUserInfoResponse
coEvery { protonMailApiManager.fetchAddresses() } throws mockException
coEvery { userManager.getUser(any(), any()) } throws mockException
val expectedResult = ListenableWorker.Result.failure(workDataOf(FETCH_USER_INFO_WORKER_EXCEPTION_MESSAGE to "exception"))
val expectedResult =
ListenableWorker.Result.failure(workDataOf(FETCH_USER_INFO_WORKER_EXCEPTION_MESSAGE to "exception"))
// when
val workerResult = worker.doWork()