FetchContactsDataWorker improved handling of CancelableException.
MAILAND-1537
This commit is contained in:
parent
8a101a41fe
commit
3dd516841b
|
@ -24,6 +24,7 @@ import androidx.test.InstrumentationRegistry
|
|||
import ch.protonmail.android.api.models.ContactEncryptedData
|
||||
import ch.protonmail.android.api.models.room.testValue
|
||||
import ch.protonmail.android.core.Constants
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.hamcrest.Matchers.`is`
|
||||
import org.junit.Assert
|
||||
import org.junit.Ignore
|
||||
|
@ -158,8 +159,10 @@ internal class ContactsDatabaseTest {
|
|||
)
|
||||
|
||||
private fun ContactsDatabase.populate() {
|
||||
saveAllContactsData(contactData)
|
||||
saveAllContactsEmailsBlocking(contactEmails)
|
||||
runBlocking {
|
||||
saveAllContactsData(contactData)
|
||||
saveAllContactsEmails(contactEmails)
|
||||
}
|
||||
fullContactDetails.forEach(this::insertFullContactDetails)
|
||||
}
|
||||
|
||||
|
@ -233,12 +236,14 @@ internal class ContactsDatabaseTest {
|
|||
|
||||
@Test
|
||||
fun saveAllContactsData() {
|
||||
val inserted = listOf(
|
||||
ContactData("y", "yy"), ContactData("z", "zz")
|
||||
)
|
||||
val expected = contactData + inserted
|
||||
database.saveAllContactsData(inserted)
|
||||
assertDatabaseState(expectedContactData = expected)
|
||||
runBlocking {
|
||||
val inserted = listOf(
|
||||
ContactData("y", "yy"), ContactData("z", "zz")
|
||||
)
|
||||
val expected = contactData + inserted
|
||||
database.saveAllContactsData(inserted)
|
||||
assertDatabaseState(expectedContactData = expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -216,7 +216,8 @@ class ProtonMailApiManager @Inject constructor(var api: ProtonMailApi) :
|
|||
|
||||
override suspend fun pingAsync(): ResponseBody = api.pingAsync()
|
||||
|
||||
override suspend fun fetchContacts(page: Int, pageSize: Int): ContactsDataResponse = api.fetchContacts(page, pageSize)
|
||||
override suspend fun fetchContacts(page: Int, pageSize: Int): ContactsDataResponse =
|
||||
api.fetchContacts(page, pageSize)
|
||||
|
||||
override suspend fun fetchContactEmails(page: Int, pageSize: Int): ContactEmailsResponseV2 =
|
||||
api.fetchContactEmails(page, pageSize)
|
||||
|
|
|
@ -63,7 +63,7 @@ interface ContactsDatabase {
|
|||
fun saveAllContactsData(vararg contactData: ContactData): List<Long>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveAllContactsData(contactData: Collection<ContactData>): List<Long>
|
||||
suspend fun saveAllContactsData(contactData: Collection<ContactData>): List<Long>
|
||||
|
||||
@Delete
|
||||
fun deleteContactData(vararg contactData: ContactData)
|
||||
|
|
|
@ -46,12 +46,16 @@ class ContactEmailsManager @Inject constructor(
|
|||
currentPage++
|
||||
}
|
||||
|
||||
val allContactEmails = allResults.flatMap { it.contactEmails }
|
||||
val allJoins = getJoins(allContactEmails)
|
||||
Timber.v(
|
||||
"Refresh emails: ${allContactEmails.size}, labels: ${contactLabelList.size}, allJoins: ${allJoins.size}"
|
||||
)
|
||||
contactsDao.insertNewContactsAndLabels(allContactEmails, contactLabelList, allJoins)
|
||||
if (allResults.isNotEmpty()) {
|
||||
val allContactEmails = allResults.flatMap { it.contactEmails }
|
||||
val allJoins = getJoins(allContactEmails)
|
||||
Timber.v(
|
||||
"Refresh emails: ${allContactEmails.size}, labels: ${contactLabelList.size}, allJoins: ${allJoins.size}"
|
||||
)
|
||||
contactsDao.insertNewContactsAndLabels(allContactEmails, contactLabelList, allJoins)
|
||||
} else {
|
||||
Timber.v("contactEmails result list is empty")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getJoins(allContactEmails: List<ContactEmail>): List<ContactEmailContactLabelJoin> {
|
||||
|
|
|
@ -35,9 +35,8 @@ import ch.protonmail.android.api.ProtonMailApiManager
|
|||
import ch.protonmail.android.api.models.room.contacts.ContactsDao
|
||||
import ch.protonmail.android.api.segments.TEN_SECONDS
|
||||
import ch.protonmail.android.core.Constants.CONTACTS_PAGE_SIZE
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.proton.core.util.kotlin.DispatcherProvider
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -55,30 +54,29 @@ class FetchContactsDataWorker @WorkerInject constructor(
|
|||
@Assisted context: Context,
|
||||
@Assisted params: WorkerParameters,
|
||||
private val api: ProtonMailApiManager,
|
||||
private val contactsDao: ContactsDao,
|
||||
private val dispatchers: DispatcherProvider
|
||||
private val contactsDao: ContactsDao
|
||||
) : CoroutineWorker(context, params) {
|
||||
|
||||
override suspend fun doWork(): Result =
|
||||
runCatching {
|
||||
withContext(dispatchers.Io) {
|
||||
Timber.v("Fetch Contacts Worker started")
|
||||
var page = 0
|
||||
var response = api.fetchContacts(page, CONTACTS_PAGE_SIZE)
|
||||
response.contacts?.let { contacts ->
|
||||
val total = response.total
|
||||
var fetched = contacts.size
|
||||
while (total > fetched) {
|
||||
++page
|
||||
response = api.fetchContacts(page, CONTACTS_PAGE_SIZE)
|
||||
val contactDataList = response.contacts
|
||||
if (contactDataList.isNullOrEmpty()) {
|
||||
break
|
||||
}
|
||||
contacts.addAll(contactDataList)
|
||||
fetched = contacts.size
|
||||
Timber.v("Fetch Contacts Worker started")
|
||||
var page = 0
|
||||
var response = api.fetchContacts(page, CONTACTS_PAGE_SIZE)
|
||||
response.contacts?.let { contacts ->
|
||||
val total = response.total
|
||||
var fetched = contacts.size
|
||||
while (total > fetched) {
|
||||
++page
|
||||
response = api.fetchContacts(page, CONTACTS_PAGE_SIZE)
|
||||
val contactDataList = response.contacts
|
||||
if (contactDataList.isNullOrEmpty()) {
|
||||
break
|
||||
}
|
||||
contacts.addAll(contactDataList)
|
||||
fetched = contacts.size
|
||||
}
|
||||
|
||||
if (contacts.isNotEmpty()) {
|
||||
contactsDao.saveAllContactsData(contacts)
|
||||
}
|
||||
}
|
||||
|
@ -91,13 +89,18 @@ class FetchContactsDataWorker @WorkerInject constructor(
|
|||
}
|
||||
)
|
||||
|
||||
private fun shouldReRunOnThrowable(throwable: Throwable): Result =
|
||||
if (runAttemptCount < MAX_RETRY_COUNT) {
|
||||
private fun shouldReRunOnThrowable(throwable: Throwable): Result {
|
||||
if (throwable is CancellationException) {
|
||||
throw throwable
|
||||
}
|
||||
|
||||
return if (runAttemptCount < MAX_RETRY_COUNT) {
|
||||
Timber.d(throwable, "Fetch Contacts Worker failure, retrying count: $runAttemptCount")
|
||||
Result.retry()
|
||||
} else {
|
||||
failure(throwable)
|
||||
}
|
||||
}
|
||||
|
||||
class Enqueuer @Inject constructor(private val workManager: WorkManager) {
|
||||
fun enqueue(): LiveData<WorkInfo> {
|
||||
|
|
|
@ -33,6 +33,7 @@ import androidx.work.WorkManager
|
|||
import androidx.work.WorkerParameters
|
||||
import ch.protonmail.android.api.segments.contact.ContactEmailsManager
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -48,8 +49,11 @@ class FetchContactsEmailsWorker @WorkerInject constructor(
|
|||
onSuccess = {
|
||||
success()
|
||||
},
|
||||
onFailure = {
|
||||
failure(it)
|
||||
onFailure = { throwable ->
|
||||
if (throwable is CancellationException) {
|
||||
throw throwable
|
||||
}
|
||||
failure(throwable)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -29,13 +29,12 @@ import ch.protonmail.android.api.models.room.contacts.ContactsDao
|
|||
import ch.protonmail.android.core.Constants
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.RelaxedMockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import me.proton.core.test.kotlin.TestDispatcherProvider
|
||||
import me.proton.core.util.android.workmanager.toWorkData
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
|
@ -60,7 +59,7 @@ class FetchContactsDataWorkerTest {
|
|||
@BeforeTest
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
worker = FetchContactsDataWorker(context, parameters, api, contactsDao, TestDispatcherProvider)
|
||||
worker = FetchContactsDataWorker(context, parameters, api, contactsDao)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -74,14 +73,14 @@ class FetchContactsDataWorkerTest {
|
|||
every { total } returns contactsList.size
|
||||
}
|
||||
coEvery { api.fetchContacts(0, Constants.CONTACTS_PAGE_SIZE) } returns response
|
||||
every { contactsDao.saveAllContactsData(contactsList) } returns listOf(1)
|
||||
coEvery { contactsDao.saveAllContactsData(contactsList) } returns listOf(1)
|
||||
val expected = ListenableWorker.Result.success()
|
||||
|
||||
// when
|
||||
val operationResult = worker.doWork()
|
||||
|
||||
// then
|
||||
verify { contactsDao.saveAllContactsData(contactsList) }
|
||||
coVerify { contactsDao.saveAllContactsData(contactsList) }
|
||||
assertEquals(expected, operationResult)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue