diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 21db20f1bf5..4e447539273 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -44,6 +44,7 @@ import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.rx2.await import kotlinx.coroutines.withContext import timber.log.Timber import java.util.Date @@ -149,7 +150,9 @@ class UploadWorker( currentNotification.build(), ) contribution!!.transferred = transferred - contributionDao.update(contribution!!).blockingAwait() + CoroutineScope(Dispatchers.IO).launch { + contributionDao.update(contribution!!).await() + } } open fun onChunkUploaded( @@ -157,7 +160,9 @@ class UploadWorker( chunkInfo: ChunkInfo?, ) { contribution.chunkInfo = chunkInfo - contributionDao.update(contribution).blockingAwait() + CoroutineScope(Dispatchers.IO).launch { + contributionDao.update(contribution).await() + } } } @@ -190,14 +195,14 @@ class UploadWorker( withContext(Dispatchers.IO) { while (contributionDao .getContribution(statesToProcess) - .blockingGet() + .await() .size > 0 && contributionDao .getContribution( arrayListOf( Contribution.STATE_IN_PROGRESS, ), - ).blockingGet() + ).await() .size == 0 ) { /* @@ -213,7 +218,7 @@ class UploadWorker( val queuedContributions = contributionDao .getContribution(statesToProcess) - .blockingGet() + .await() // Showing initial notification for the number of uploads being processed processingUploads.setContentTitle(appContext.getString(R.string.starting_uploads)) @@ -253,7 +258,7 @@ class UploadWorker( // Trigger WorkManager to process any new contributions that may have been added to the queue val updatedContributionQueue = withContext(Dispatchers.IO) { - contributionDao.getContribution(statesToProcess).blockingGet() + contributionDao.getContribution(statesToProcess).await() } if (updatedContributionQueue.isNotEmpty()) { return Result.retry() @@ -353,7 +358,7 @@ class UploadWorker( fileKey = null, errorMessage = it.message, ) - }.blockingSingle() + }.firstOrError().await() when (stashUploadResult.state) { StashUploadState.SUCCESS -> { // If the stash upload succeeds @@ -369,9 +374,7 @@ class UploadWorker( contribution, uniqueFileName, stashUploadResult.fileKey, - ).onErrorReturn { - return@onErrorReturn null - }.blockingSingle() + ).firstOrError().await() if (null != uploadResult && uploadResult.isSuccessful()) { Timber.d( @@ -380,7 +383,7 @@ class UploadWorker( wikidataEditService .addDepictionsAndCaptions(uploadResult, contribution) - .blockingSubscribe() + .firstOrError().await() if (contribution.wikidataPlace == null) { Timber.d( "WikiDataEdit not required, upload success", @@ -404,7 +407,7 @@ class UploadWorker( showFailedNotification(contribution) contribution.state = Contribution.STATE_FAILED contribution.chunkInfo = null - contributionDao.save(contribution).blockingAwait() + contributionDao.save(contribution).await() } } catch (exception: Exception) { Timber.e(exception) @@ -496,7 +499,7 @@ class UploadWorker( placesRepository .save(place) .subscribeOn(Schedulers.io()) - .blockingAwait() + .await() Timber.d("Updated WikiItem place ${place.name} with image ${place.pic}") } } @@ -526,7 +529,7 @@ class UploadWorker( saveCompletedContribution(contribution, uploadResult) } - private fun saveCompletedContribution( + private suspend fun saveCompletedContribution( contribution: Contribution, uploadResult: UploadResult, ) { @@ -534,7 +537,7 @@ class UploadWorker( mediaClient .getMedia("File:" + uploadResult.filename) .map { media: Media? -> contribution.completeWith(media!!) } - .blockingGet() + .await() contributionFromUpload.dateModified = Date() contributionDao.deleteAndSaveContribution(contribution, contributionFromUpload) @@ -562,7 +565,7 @@ class UploadWorker( } } - private fun findUniqueFileName(fileName: String): String { + private suspend fun findUniqueFileName(fileName: String): String { var sequenceFileName: String? = fileName val random = Random() @@ -573,7 +576,7 @@ class UploadWorker( "File:%s", sequenceFileName, ), - ).blockingGet()) { + ).await()) { // Generate a random 5-character alphanumeric string val randomHash = (random.nextInt(90000) + 10000).toString() diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt index f4bf230734b..587940330aa 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.kt @@ -27,6 +27,7 @@ import fr.free.nrw.commons.wikidata.model.WikiBaseMonolingualTextValue import fr.free.nrw.commons.wikidata.mwapi.MwPostResponse import io.reactivex.Observable import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.rx2.await import timber.log.Timber import java.util.Arrays import java.util.Collections @@ -80,37 +81,37 @@ class WikidataEditService @Inject constructor( depictedItems: List ): Observable { val entityId: String = PAGE_ID_PREFIX + fileEntityId - val claimIds = getDepictionsClaimIds(entityId) + return getDepictionsClaimIds(entityId).flatMap { claimIds -> - /* Please consider removeClaim scenario for BetaDebug */ - val data = RemoveClaim.from(if (isBetaFlavour) listOf("Q10") else claimIds) + /* Please consider removeClaim scenario for BetaDebug */ + val data = RemoveClaim.from(if (isBetaFlavour) listOf("Q10") else claimIds) - return wikiBaseClient.postDeleteClaims(entityId, gson.toJson(data)) - .doOnError { throwable: Throwable? -> - Timber.e( - throwable, - "Error occurred while removing existing claims for DEPICTS property" - ) - showLongToast( - context, - context.getString(R.string.wikidata_edit_failure) - ) - }.switchMap { success: Boolean -> - if (success) { - Timber.d("DEPICTS property was deleted successfully") - return@switchMap addDepictsProperty(fileEntityId!!, depictedItems) - } else { - Timber.d("Unable to delete DEPICTS property") - return@switchMap Observable.empty() + wikiBaseClient.postDeleteClaims(entityId, gson.toJson(data)) + .doOnError { throwable: Throwable? -> + Timber.e( + throwable, + "Error occurred while removing existing claims for DEPICTS property" + ) + showLongToast( + context, + context.getString(R.string.wikidata_edit_failure) + ) + }.switchMap { success: Boolean -> + if (success) { + Timber.d("DEPICTS property was deleted successfully") + return@switchMap addDepictsProperty(fileEntityId!!, depictedItems) + } else { + Timber.d("Unable to delete DEPICTS property") + return@switchMap Observable.empty() + } } - } + } } @SuppressLint("CheckResult") - private fun getDepictionsClaimIds(entityId: String): List { + private fun getDepictionsClaimIds(entityId: String): Observable> { return wikiBaseClient.getClaimIdsByProperty(entityId, DEPICTS.propertyName) .subscribeOn(Schedulers.io()) - .blockingFirst() } @SuppressLint("StringFormatInvalid") @@ -151,7 +152,7 @@ class WikidataEditService @Inject constructor( } } - fun createClaim( + suspend fun createClaim( wikidataPlace: WikidataPlace?, fileName: String, captions: Map ): Long? { @@ -164,7 +165,7 @@ class WikidataEditService @Inject constructor( return addImageAndMediaLegends(wikidataPlace!!, fileName, captions) } - fun addImageAndMediaLegends( + suspend fun addImageAndMediaLegends( wikidataItem: WikidataItem, fileName: String, captions: Map ): Long { @@ -193,7 +194,7 @@ class WikidataEditService @Inject constructor( ), Arrays.asList(MEDIA_LEGENDS.propertyName) ) - return wikidataClient.setClaim(claim, COMMONS_APP_TAG).blockingSingle() + return wikidataClient.setClaim(claim, COMMONS_APP_TAG).firstOrError().await() } fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?, p18WasSkipped: Boolean = false) { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt index c22195c7c65..4013e03fb25 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/wikidata/WikidataEditServiceTest.kt @@ -10,16 +10,19 @@ import fr.free.nrw.commons.upload.UploadResult import fr.free.nrw.commons.upload.WikidataPlace import fr.free.nrw.commons.wikidata.model.RemoveClaim import io.reactivex.Observable +import kotlinx.coroutines.runBlocking import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyString import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.verifyNoInteractions -import org.mockito.MockitoAnnotations +import org.mockito.junit.MockitoJUnitRunner +@RunWith(MockitoJUnitRunner::class) class WikidataEditServiceTest { @Mock internal lateinit var context: Context @@ -41,18 +44,18 @@ class WikidataEditServiceTest { @Before @Throws(Exception::class) - fun setUp() { - MockitoAnnotations.openMocks(this) + fun setUp(): Unit { + } @Test - fun noClaimsWhenEntityIdIsNull() { + fun noClaimsWhenEntityIdIsNull(): Unit = runBlocking { wikidataEditService.createClaim(mock(), "Test.jpg", hashMapOf()) verifyNoInteractions(wikidataClient) } @Test - fun testUpdateDepictsProperty() { + fun testUpdateDepictsProperty(): Unit { val fileEntityId = "12345" whenever( @@ -72,7 +75,7 @@ class WikidataEditServiceTest { } @Test - fun createImageClaim() { + fun createImageClaim(): Unit = runBlocking { whenever(directKvStore.getBoolean("Picture_Has_Correct_Location", true)) .thenReturn(true) whenever(wikibaseClient.getFileEntityId(anyOrNull())).thenReturn(Observable.just(1L)) @@ -86,5 +89,6 @@ class WikidataEditServiceTest { uploadResult.filename, hashMapOf(), ) + Unit } }