From f50bf3e9c2f57af1222a6ef74d23896b275c2e3c Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 28 Jul 2022 15:54:49 -0300 Subject: [PATCH] Remove blocking get from donation jobs. --- .../badges/gifts/flow/GiftFlowRepository.kt | 14 +- .../gifts/viewgift/ViewGiftRepository.kt | 9 +- .../donor/DonorErrorConfigurationViewModel.kt | 21 ++- .../subscription/DonationPaymentRepository.kt | 155 ++++++++++-------- .../subscription/SubscriptionsRepository.kt | 5 +- .../app/subscription/boost/BoostRepository.kt | 9 +- .../detail/DonationReceiptDetailRepository.kt | 9 +- .../list/DonationReceiptListRepository.kt | 9 +- .../delete/DeleteAccountRepository.java | 3 +- .../securesms/glide/GiftBadgeModel.kt | 2 +- .../jobs/BoostReceiptRequestResponseJob.java | 3 +- .../jobs/DonationReceiptRedemptionJob.java | 3 +- .../securesms/jobs/RefreshOwnProfileJob.java | 3 +- .../jobs/SubscriptionKeepAliveJob.java | 6 +- ...SubscriptionReceiptRequestResponseJob.java | 6 +- .../api/services/DonationsService.java | 113 ++++++------- .../api/services/DonationsServiceTest.java | 27 +-- 17 files changed, 214 insertions(+), 183 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt index e5a69dc10..f96747f30 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowRepository.kt @@ -18,8 +18,11 @@ import java.util.Locale class GiftFlowRepository { fun getGiftBadge(): Single> { - return ApplicationDependencies.getDonationsService() - .getGiftBadges(Locale.getDefault()) + return Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .getGiftBadges(Locale.getDefault()) + } .flatMap(ServiceResponse>::flattenResult) .map { gifts -> gifts.map { it.key to Badges.fromServiceBadge(it.value) } } .map { it.first() } @@ -27,8 +30,11 @@ class GiftFlowRepository { } fun getGiftPricing(): Single> { - return ApplicationDependencies.getDonationsService() - .giftAmount + return Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .giftAmount + } .subscribeOn(Schedulers.io()) .flatMap { it.flattenResult() } .map { result -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/viewgift/ViewGiftRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/viewgift/ViewGiftRepository.kt index e83286f74..4707138c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/viewgift/ViewGiftRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/viewgift/ViewGiftRepository.kt @@ -19,9 +19,12 @@ import java.util.Locale class ViewGiftRepository { fun getBadge(giftBadge: GiftBadge): Single { val presentation = ReceiptCredentialPresentation(giftBadge.redemptionToken.toByteArray()) - return ApplicationDependencies - .getDonationsService() - .getGiftBadge(Locale.getDefault(), presentation.receiptLevel) + return Single + .fromCallable { + ApplicationDependencies + .getDonationsService() + .getGiftBadge(Locale.getDefault(), presentation.receiptLevel) + } .flatMap { it.flattenResult() } .map { Badges.fromServiceBadge(it) } .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/DonorErrorConfigurationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/DonorErrorConfigurationViewModel.kt index 9e40f6203..bd3205f6e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/DonorErrorConfigurationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/donor/DonorErrorConfigurationViewModel.kt @@ -26,20 +26,29 @@ class DonorErrorConfigurationViewModel : ViewModel() { val state: Flowable = store.stateFlowable init { - val giftBadges: Single> = ApplicationDependencies.getDonationsService() - .getGiftBadges(Locale.getDefault()) + val giftBadges: Single> = Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .getGiftBadges(Locale.getDefault()) + } .flatMap { it.flattenResult() } .map { results -> results.values.map { Badges.fromServiceBadge(it) } } .subscribeOn(Schedulers.io()) - val boostBadges: Single> = ApplicationDependencies.getDonationsService() - .getBoostBadge(Locale.getDefault()) + val boostBadges: Single> = Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .getBoostBadge(Locale.getDefault()) + } .flatMap { it.flattenResult() } .map { listOf(Badges.fromServiceBadge(it)) } .subscribeOn(Schedulers.io()) - val subscriptionBadges: Single> = ApplicationDependencies.getDonationsService() - .getSubscriptionLevels(Locale.getDefault()) + val subscriptionBadges: Single> = Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .getSubscriptionLevels(Locale.getDefault()) + } .flatMap { it.flattenResult() } .map { levels -> levels.levels.values.map { Badges.fromServiceBadge(it.badge) } } .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt index 75fd83712..0a316fc44 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/DonationPaymentRepository.kt @@ -170,8 +170,11 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet fun cancelActiveSubscription(): Completable { Log.d(TAG, "Canceling active subscription...", true) val localSubscriber = SignalStore.donationsValues().requireSubscriber() - return ApplicationDependencies.getDonationsService() - .cancelSubscription(localSubscriber.subscriberId) + return Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .cancelSubscription(localSubscriber.subscriberId) + } .subscribeOn(Schedulers.io()) .flatMap(ServiceResponse::flattenResult) .ignoreElement() @@ -181,9 +184,12 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet fun ensureSubscriberId(): Completable { Log.d(TAG, "Ensuring SubscriberId exists on Signal service...", true) val subscriberId = SignalStore.donationsValues().getSubscriber()?.subscriberId ?: SubscriberId.generate() - return ApplicationDependencies - .getDonationsService() - .putSubscription(subscriberId) + return Single + .fromCallable { + ApplicationDependencies + .getDonationsService() + .putSubscription(subscriberId) + } .subscribeOn(Schedulers.io()) .flatMap(ServiceResponse::flattenResult).ignoreElement() .doOnComplete { @@ -268,67 +274,71 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet val subscriber = SignalStore.donationsValues().requireSubscriber() Log.d(TAG, "Attempting to set user subscription level to $subscriptionLevel", true) - ApplicationDependencies.getDonationsService().updateSubscriptionLevel( - subscriber.subscriberId, - subscriptionLevel, - subscriber.currencyCode, - levelUpdateOperation.idempotencyKey.serialize(), - SubscriptionReceiptRequestResponseJob.MUTEX - ).flatMapCompletable { - if (it.status == 200 || it.status == 204) { - Log.d(TAG, "Successfully set user subscription to level $subscriptionLevel with response code ${it.status}", true) - SignalStore.donationsValues().updateLocalStateForLocalSubscribe() - scheduleSyncForAccountRecordChange() - LevelUpdate.updateProcessingState(false) - Completable.complete() - } else { - if (it.applicationError.isPresent) { - Log.w(TAG, "Failed to set user subscription to level $subscriptionLevel with response code ${it.status}", it.applicationError.get(), true) - SignalStore.donationsValues().clearLevelOperations() + Single + .fromCallable { + ApplicationDependencies.getDonationsService().updateSubscriptionLevel( + subscriber.subscriberId, + subscriptionLevel, + subscriber.currencyCode, + levelUpdateOperation.idempotencyKey.serialize(), + SubscriptionReceiptRequestResponseJob.MUTEX + ) + } + .flatMapCompletable { + if (it.status == 200 || it.status == 204) { + Log.d(TAG, "Successfully set user subscription to level $subscriptionLevel with response code ${it.status}", true) + SignalStore.donationsValues().updateLocalStateForLocalSubscribe() + scheduleSyncForAccountRecordChange() + LevelUpdate.updateProcessingState(false) + Completable.complete() } else { - Log.w(TAG, "Failed to set user subscription to level $subscriptionLevel", it.executionError.orElse(null), true) - } - - LevelUpdate.updateProcessingState(false) - it.flattenResult().ignoreElement() - } - }.andThen { - Log.d(TAG, "Enqueuing request response job chain.", true) - val countDownLatch = CountDownLatch(1) - var finalJobState: JobTracker.JobState? = null - - SubscriptionReceiptRequestResponseJob.createSubscriptionContinuationJobChain().enqueue { _, jobState -> - if (jobState.isComplete) { - finalJobState = jobState - countDownLatch.countDown() - } - } - - try { - if (countDownLatch.await(10, TimeUnit.SECONDS)) { - when (finalJobState) { - JobTracker.JobState.SUCCESS -> { - Log.d(TAG, "Subscription request response job chain succeeded.", true) - it.onComplete() - } - JobTracker.JobState.FAILURE -> { - Log.d(TAG, "Subscription request response job chain failed permanently.", true) - it.onError(DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)) - } - else -> { - Log.d(TAG, "Subscription request response job chain ignored due to in-progress jobs.", true) - it.onError(DonationError.timeoutWaitingForToken(DonationErrorSource.SUBSCRIPTION)) - } + if (it.applicationError.isPresent) { + Log.w(TAG, "Failed to set user subscription to level $subscriptionLevel with response code ${it.status}", it.applicationError.get(), true) + SignalStore.donationsValues().clearLevelOperations() + } else { + Log.w(TAG, "Failed to set user subscription to level $subscriptionLevel", it.executionError.orElse(null), true) } - } else { - Log.d(TAG, "Subscription request response job timed out.", true) + + LevelUpdate.updateProcessingState(false) + it.flattenResult().ignoreElement() + } + }.andThen { + Log.d(TAG, "Enqueuing request response job chain.", true) + val countDownLatch = CountDownLatch(1) + var finalJobState: JobTracker.JobState? = null + + SubscriptionReceiptRequestResponseJob.createSubscriptionContinuationJobChain().enqueue { _, jobState -> + if (jobState.isComplete) { + finalJobState = jobState + countDownLatch.countDown() + } + } + + try { + if (countDownLatch.await(10, TimeUnit.SECONDS)) { + when (finalJobState) { + JobTracker.JobState.SUCCESS -> { + Log.d(TAG, "Subscription request response job chain succeeded.", true) + it.onComplete() + } + JobTracker.JobState.FAILURE -> { + Log.d(TAG, "Subscription request response job chain failed permanently.", true) + it.onError(DonationError.genericBadgeRedemptionFailure(DonationErrorSource.SUBSCRIPTION)) + } + else -> { + Log.d(TAG, "Subscription request response job chain ignored due to in-progress jobs.", true) + it.onError(DonationError.timeoutWaitingForToken(DonationErrorSource.SUBSCRIPTION)) + } + } + } else { + Log.d(TAG, "Subscription request response job timed out.", true) + it.onError(DonationError.timeoutWaitingForToken(DonationErrorSource.SUBSCRIPTION)) + } + } catch (e: InterruptedException) { + Log.w(TAG, "Subscription request response interrupted.", e, true) it.onError(DonationError.timeoutWaitingForToken(DonationErrorSource.SUBSCRIPTION)) } - } catch (e: InterruptedException) { - Log.w(TAG, "Subscription request response interrupted.", e, true) - it.onError(DonationError.timeoutWaitingForToken(DonationErrorSource.SUBSCRIPTION)) } - } }.doOnError { LevelUpdate.updateProcessingState(false) }.subscribeOn(Schedulers.io()) @@ -356,9 +366,12 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet override fun fetchPaymentIntent(price: FiatMoney, level: Long): Single { Log.d(TAG, "Fetching payment intent from Signal service for $price... (Locale.US minimum precision: ${price.minimumUnitPrecisionString})") - return ApplicationDependencies - .getDonationsService() - .createDonationIntentWithAmount(price.minimumUnitPrecisionString, price.currency.currencyCode, level) + return Single + .fromCallable { + ApplicationDependencies + .getDonationsService() + .createDonationIntentWithAmount(price.minimumUnitPrecisionString, price.currency.currencyCode, level) + } .flatMap(ServiceResponse::flattenResult) .map { StripeApi.PaymentIntent(it.id, it.clientSecret) @@ -370,7 +383,13 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet override fun fetchSetupIntent(): Single { Log.d(TAG, "Fetching setup intent from Signal service...") return Single.fromCallable { SignalStore.donationsValues().requireSubscriber() } - .flatMap { ApplicationDependencies.getDonationsService().createSubscriptionPaymentMethod(it.subscriberId) } + .flatMap { + Single.fromCallable { + ApplicationDependencies + .getDonationsService() + .createSubscriptionPaymentMethod(it.subscriberId) + } + } .flatMap(ServiceResponse::flattenResult) .map { StripeApi.SetupIntent(it.id, it.clientSecret) } .doOnSuccess { @@ -383,7 +402,11 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet return Single.fromCallable { SignalStore.donationsValues().requireSubscriber() }.flatMap { - ApplicationDependencies.getDonationsService().setDefaultPaymentMethodId(it.subscriberId, paymentMethodId) + Single.fromCallable { + ApplicationDependencies + .getDonationsService() + .setDefaultPaymentMethodId(it.subscriberId, paymentMethodId) + } }.flatMap(ServiceResponse::flattenResult).ignoreElement().doOnComplete { Log.d(TAG, "Set default payment method via Signal service!") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsRepository.kt index 26670bbaa..67e242e10 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/SubscriptionsRepository.kt @@ -23,7 +23,7 @@ class SubscriptionsRepository(private val donationsService: DonationsService) { fun getActiveSubscription(): Single { val localSubscription = SignalStore.donationsValues().getSubscriber() return if (localSubscription != null) { - donationsService.getSubscription(localSubscription.subscriberId) + Single.fromCallable { donationsService.getSubscription(localSubscription.subscriberId) } .subscribeOn(Schedulers.io()) .flatMap(ServiceResponse::flattenResult) } else { @@ -31,7 +31,8 @@ class SubscriptionsRepository(private val donationsService: DonationsService) { } } - fun getSubscriptions(): Single> = donationsService.getSubscriptionLevels(Locale.getDefault()) + fun getSubscriptions(): Single> = Single + .fromCallable { donationsService.getSubscriptionLevels(Locale.getDefault()) } .subscribeOn(Schedulers.io()) .flatMap(ServiceResponse::flattenResult) .map { subscriptionLevels -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostRepository.kt index ec573277f..c7e2bfa44 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/boost/BoostRepository.kt @@ -5,6 +5,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers import org.signal.core.util.money.FiatMoney import org.thoughtcrime.securesms.badges.Badges import org.thoughtcrime.securesms.badges.models.Badge +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.util.PlatformCurrencyUtil import org.whispersystems.signalservice.api.profiles.SignalServiceProfile import org.whispersystems.signalservice.api.services.DonationsService @@ -16,7 +17,7 @@ import java.util.Locale class BoostRepository(private val donationsService: DonationsService) { fun getBoosts(): Single>> { - return donationsService.boostAmounts + return Single.fromCallable { donationsService.boostAmounts } .subscribeOn(Schedulers.io()) .flatMap(ServiceResponse>>::flattenResult) .map { result -> @@ -28,7 +29,11 @@ class BoostRepository(private val donationsService: DonationsService) { } fun getBoostBadge(): Single { - return donationsService.getBoostBadge(Locale.getDefault()) + return Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .getBoostBadge(Locale.getDefault()) + } .subscribeOn(Schedulers.io()) .flatMap(ServiceResponse::flattenResult) .map(Badges::fromServiceBadge) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/detail/DonationReceiptDetailRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/detail/DonationReceiptDetailRepository.kt index 86b57dabf..548e852e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/detail/DonationReceiptDetailRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/detail/DonationReceiptDetailRepository.kt @@ -9,9 +9,12 @@ import java.util.Locale class DonationReceiptDetailRepository { fun getSubscriptionLevelName(subscriptionLevel: Int): Single { - return ApplicationDependencies - .getDonationsService() - .getSubscriptionLevels(Locale.getDefault()) + return Single + .fromCallable { + ApplicationDependencies + .getDonationsService() + .getSubscriptionLevels(Locale.getDefault()) + } .flatMap { it.flattenResult() } .map { it.levels[subscriptionLevel.toString()] ?: throw Exception("Subscription level $subscriptionLevel not found") } .map { it.name } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt index a444136c6..f24e31cfe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/receipts/list/DonationReceiptListRepository.kt @@ -9,7 +9,11 @@ import java.util.Locale class DonationReceiptListRepository { fun getBadges(): Single> { - val boostBadges: Single> = ApplicationDependencies.getDonationsService().getBoostBadge(Locale.getDefault()) + val boostBadges: Single> = Single + .fromCallable { + ApplicationDependencies.getDonationsService() + .getBoostBadge(Locale.getDefault()) + } .map { response -> if (response.result.isPresent) { listOf(DonationReceiptBadge(DonationReceiptRecord.Type.BOOST, -1, Badges.fromServiceBadge(response.result.get()))) @@ -18,7 +22,8 @@ class DonationReceiptListRepository { } } - val subBadges: Single> = ApplicationDependencies.getDonationsService().getSubscriptionLevels(Locale.getDefault()) + val subBadges: Single> = Single + .fromCallable { ApplicationDependencies.getDonationsService().getSubscriptionLevels(Locale.getDefault()) } .map { response -> if (response.result.isPresent) { response.result.get().levels.map { diff --git a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java index 62d272823..d98c9ec79 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/delete/DeleteAccountRepository.java @@ -52,8 +52,7 @@ class DeleteAccountRepository { Subscriber subscriber = SignalStore.donationsValues().requireSubscriber(); ServiceResponse cancelSubscriptionResponse = ApplicationDependencies.getDonationsService() - .cancelSubscription(subscriber.getSubscriberId()) - .blockingGet(); + .cancelSubscription(subscriber.getSubscriberId()); if (cancelSubscriptionResponse.getExecutionError().isPresent()) { Log.w(TAG, "deleteAccount: failed attempt to cancel subscription"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt b/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt index 1c2ddc41b..d42489aa8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/glide/GiftBadgeModel.kt @@ -47,7 +47,7 @@ data class GiftBadgeModel(val giftBadge: GiftBadge) : Key { override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { try { val receiptCredentialPresentation = ReceiptCredentialPresentation(giftBadge.giftBadge.redemptionToken.toByteArray()) - val giftBadgeResponse = ApplicationDependencies.getDonationsService().getGiftBadge(Locale.getDefault(), receiptCredentialPresentation.receiptLevel).blockingGet() + val giftBadgeResponse = ApplicationDependencies.getDonationsService().getGiftBadge(Locale.getDefault(), receiptCredentialPresentation.receiptLevel) if (giftBadgeResponse.result.isPresent) { val badge = Badges.fromServiceBadge(giftBadgeResponse.result.get()) okHttpStreamFetcher = OkHttpStreamFetcher(client, GlideUrl(badge.imageUrl.toString())) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java index 44b06896f..a3bf0f8f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/BoostReceiptRequestResponseJob.java @@ -153,8 +153,7 @@ public class BoostReceiptRequestResponseJob extends BaseJob { Log.d(TAG, "Submitting credential to server", true); ServiceResponse response = ApplicationDependencies.getDonationsService() - .submitBoostReceiptCredentialRequest(paymentIntentId, requestContext.getRequest()) - .blockingGet(); + .submitBoostReceiptCredentialRequestSync(paymentIntentId, requestContext.getRequest()); if (response.getApplicationError().isPresent()) { handleApplicationError(context, response, donationErrorSource); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java index 96bdcda9d..4cb2170ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/DonationReceiptRedemptionJob.java @@ -149,8 +149,7 @@ public class DonationReceiptRedemptionJob extends BaseJob { ServiceResponse response = ApplicationDependencies.getDonationsService() .redeemReceipt(presentation, SignalStore.donationsValues().getDisplayBadgesOnProfile(), - makePrimary) - .blockingGet(); + makePrimary); if (response.getApplicationError().isPresent()) { if (response.getStatus() >= 500) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index ed35b60d8..0323db41f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -276,8 +276,7 @@ public class RefreshOwnProfileJob extends BaseJob { boolean isDueToPaymentFailure = false; if (subscriber != null) { ServiceResponse response = ApplicationDependencies.getDonationsService() - .getSubscription(subscriber.getSubscriberId()) - .blockingGet(); + .getSubscription(subscriber.getSubscriberId()); if (response.getResult().isPresent()) { ActiveSubscription activeSubscription = response.getResult().get(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java index 59f3a493b..c40d9927c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java @@ -86,15 +86,13 @@ public class SubscriptionKeepAliveJob extends BaseJob { } ServiceResponse response = ApplicationDependencies.getDonationsService() - .putSubscription(subscriber.getSubscriberId()) - .blockingGet(); + .putSubscription(subscriber.getSubscriberId()); verifyResponse(response); Log.i(TAG, "Successful call to PUT subscription ID", true); ServiceResponse activeSubscriptionResponse = ApplicationDependencies.getDonationsService() - .getSubscription(subscriber.getSubscriberId()) - .blockingGet(); + .getSubscription(subscriber.getSubscriberId()); verifyResponse(activeSubscriptionResponse); Log.i(TAG, "Successful call to GET active subscription", true); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java index 651dd998f..ca90c0b49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java @@ -187,8 +187,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { Log.d(TAG, "Submitting receipt credential request."); ServiceResponse response = ApplicationDependencies.getDonationsService() - .submitReceiptCredentialRequest(subscriberId, requestContext.getRequest()) - .blockingGet(); + .submitReceiptCredentialRequestSync(subscriberId, requestContext.getRequest()); if (response.getApplicationError().isPresent()) { handleApplicationError(response); @@ -216,8 +215,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { private @NonNull ActiveSubscription getLatestSubscriptionInformation() throws Exception { ServiceResponse activeSubscription = ApplicationDependencies.getDonationsService() - .getSubscription(subscriberId) - .blockingGet(); + .getSubscription(subscriberId); if (activeSubscription.getResult().isPresent()) { return activeSubscription.getResult().get(); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java index 9668a8908..9d9d0300f 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/services/DonationsService.java @@ -20,7 +20,6 @@ import org.whispersystems.signalservice.internal.push.PushServiceSocket; import java.io.IOException; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -28,8 +27,6 @@ import java.util.Objects; import java.util.TreeMap; import io.reactivex.rxjava3.annotations.NonNull; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.schedulers.Schedulers; /** * One-stop shop for Signal service calls related to donations. @@ -62,15 +59,13 @@ public class DonationsService { * @param visible Whether the badge will be visible on the user's profile immediately after redemption * @param primary Whether the badge will be made primary immediately after redemption */ - public Single> redeemReceipt(ReceiptCredentialPresentation receiptCredentialPresentation, boolean visible, boolean primary) { - return Single.fromCallable(() -> { - try { - pushServiceSocket.redeemDonationReceipt(receiptCredentialPresentation, visible, primary); - return ServiceResponse.forResult(EmptyResponse.INSTANCE, 200, null); - } catch (Exception e) { - return ServiceResponse.forUnknownError(e); - } - }).subscribeOn(Schedulers.io()); + public ServiceResponse redeemReceipt(ReceiptCredentialPresentation receiptCredentialPresentation, boolean visible, boolean primary) { + try { + pushServiceSocket.redeemDonationReceipt(receiptCredentialPresentation, visible, primary); + return ServiceResponse.forResult(EmptyResponse.INSTANCE, 200, null); + } catch (Exception e) { + return ServiceResponse.forUnknownError(e); + } } /** @@ -80,8 +75,8 @@ public class DonationsService { * @param currencyCode The currency code for the amount * @return A ServiceResponse containing a DonationIntentResult with details given to us by the payment gateway. */ - public Single> createDonationIntentWithAmount(String amount, String currencyCode, long level) { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.createBoostPaymentMethod(currencyCode, Long.parseLong(amount), level), 200)); + public ServiceResponse createDonationIntentWithAmount(String amount, String currencyCode, long level) { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.createBoostPaymentMethod(currencyCode, Long.parseLong(amount), level), 200)); } /** @@ -91,36 +86,36 @@ public class DonationsService { * @param paymentIntentId PaymentIntent ID from a boost donation intent response. * @param receiptCredentialRequest Client-generated request token */ - public Single> submitBoostReceiptCredentialRequest(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest) { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.submitBoostReceiptCredentials(paymentIntentId, receiptCredentialRequest), 200)); + public ServiceResponse submitBoostReceiptCredentialRequestSync(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest) { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.submitBoostReceiptCredentials(paymentIntentId, receiptCredentialRequest), 200)); } /** * @return The suggested amounts for Signal Boost */ - public Single>>> getBoostAmounts() { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostAmounts(), 200)); + public ServiceResponse>> getBoostAmounts() { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostAmounts(), 200)); } /** * @return The badge configuration for signal boost. Expect for right now only a single level numbered 1. */ - public Single> getBoostBadge(Locale locale) { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(SubscriptionLevels.BOOST_LEVEL).getBadge(), 200)); + public ServiceResponse getBoostBadge(Locale locale) { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(SubscriptionLevels.BOOST_LEVEL).getBadge(), 200)); } /** * @return A specific gift badge, by level. */ - public Single> getGiftBadge(Locale locale, long level) { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(String.valueOf(level)).getBadge(), 200)); + public ServiceResponse getGiftBadge(Locale locale, long level) { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(String.valueOf(level)).getBadge(), 200)); } /** * @return All gift badges the server currently has available. */ - public Single>> getGiftBadges(Locale locale) { - return createServiceResponse(() -> { + public ServiceResponse> getGiftBadges(Locale locale) { + return wrapInServiceResponse(() -> { Map levels = pushServiceSocket.getBoostLevels(locale).getLevels(); Map badges = new TreeMap<>(); @@ -141,15 +136,15 @@ public class DonationsService { /** * Returns the amounts for the gift badge. */ - public Single>> getGiftAmount() { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.getGiftAmount(), 200)); + public ServiceResponse> getGiftAmount() { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getGiftAmount(), 200)); } /** * Returns the subscription levels that are available for the client to choose from along with currencies and current prices */ - public Single> getSubscriptionLevels(Locale locale) { - return createServiceResponse(() -> new Pair<>(pushServiceSocket.getSubscriptionLevels(locale), 200)); + public ServiceResponse getSubscriptionLevels(Locale locale) { + return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.getSubscriptionLevels(locale), 200)); } /** @@ -165,13 +160,13 @@ public class DonationsService { * @param idempotencyKey url-safe-base64-encoded random 16-byte value (see description) * @param mutex A mutex to lock on to avoid a situation where this subscription update happens *as* we are trying to get a credential receipt. */ - public Single> updateSubscriptionLevel(SubscriberId subscriberId, - String level, - String currencyCode, - String idempotencyKey, - Object mutex + public ServiceResponse updateSubscriptionLevel(SubscriberId subscriberId, + String level, + String currencyCode, + String idempotencyKey, + Object mutex ) { - return createServiceResponse(() -> { + return wrapInServiceResponse(() -> { synchronized(mutex) { pushServiceSocket.updateSubscriptionLevel(subscriberId.serialize(), level, currencyCode, idempotencyKey); } @@ -180,10 +175,10 @@ public class DonationsService { } /** - * Returns information about the current subscription if one exists. + * Synchronously returns information about the current subscription if one exists. */ - public Single> getSubscription(SubscriberId subscriberId) { - return createServiceResponse(() -> { + public ServiceResponse getSubscription(SubscriberId subscriberId) { + return wrapInServiceResponse(() -> { ActiveSubscription response = pushServiceSocket.getSubscription(subscriberId.serialize()); return new Pair<>(response, 200); }); @@ -199,8 +194,8 @@ public class DonationsService { * * @param subscriberId The subscriber ID for the user polling their subscription */ - public Single> putSubscription(SubscriberId subscriberId) { - return createServiceResponse(() -> { + public ServiceResponse putSubscription(SubscriberId subscriberId) { + return wrapInServiceResponse(() -> { pushServiceSocket.putSubscription(subscriberId.serialize()); return new Pair<>(EmptyResponse.INSTANCE, 200); }); @@ -211,15 +206,15 @@ public class DonationsService { * * @param subscriberId The subscriber ID for the user cancelling their subscription */ - public Single> cancelSubscription(SubscriberId subscriberId) { - return createServiceResponse(() -> { + public ServiceResponse cancelSubscription(SubscriberId subscriberId) { + return wrapInServiceResponse(() -> { pushServiceSocket.deleteSubscription(subscriberId.serialize()); return new Pair<>(EmptyResponse.INSTANCE, 200); }); } - public Single> setDefaultPaymentMethodId(SubscriberId subscriberId, String paymentMethodId) { - return createServiceResponse(() -> { + public ServiceResponse setDefaultPaymentMethodId(SubscriberId subscriberId, String paymentMethodId) { + return wrapInServiceResponse(() -> { pushServiceSocket.setDefaultSubscriptionPaymentMethod(subscriberId.serialize(), paymentMethodId); return new Pair<>(EmptyResponse.INSTANCE, 200); }); @@ -231,33 +226,31 @@ public class DonationsService { * @return Client secret for a SetupIntent. It should not be used with the PaymentIntent stripe APIs * but instead with the SetupIntent stripe APIs. */ - public Single> createSubscriptionPaymentMethod(SubscriberId subscriberId) { - return createServiceResponse(() -> { + public ServiceResponse createSubscriptionPaymentMethod(SubscriberId subscriberId) { + return wrapInServiceResponse(() -> { SubscriptionClientSecret clientSecret = pushServiceSocket.createSubscriptionPaymentMethod(subscriberId.serialize()); return new Pair<>(clientSecret, 200); }); } - public Single> submitReceiptCredentialRequest(SubscriberId subscriberId, ReceiptCredentialRequest receiptCredentialRequest) { - return createServiceResponse(() -> { + public ServiceResponse submitReceiptCredentialRequestSync(SubscriberId subscriberId, ReceiptCredentialRequest receiptCredentialRequest) { + return wrapInServiceResponse(() -> { ReceiptCredentialResponse response = pushServiceSocket.submitReceiptCredentials(subscriberId.serialize(), receiptCredentialRequest); return new Pair<>(response, 200); }); } - private Single> createServiceResponse(Producer producer) { - return Single.fromCallable(() -> { - try { - Pair responseAndCode = producer.produce(); - return ServiceResponse.forResult(responseAndCode.first(), responseAndCode.second(), null); - } catch (NonSuccessfulResponseCodeException e) { - Log.w(TAG, "Bad response code from server.", e); - return ServiceResponse.forApplicationError(e, e.getCode(), e.getMessage()); - } catch (IOException e) { - Log.w(TAG, "An unknown error occurred.", e); - return ServiceResponse.forUnknownError(e); - } - }); + private ServiceResponse wrapInServiceResponse(Producer producer) { + try { + Pair responseAndCode = producer.produce(); + return ServiceResponse.forResult(responseAndCode.first(), responseAndCode.second(), null); + } catch (NonSuccessfulResponseCodeException e) { + Log.w(TAG, "Bad response code from server.", e); + return ServiceResponse.forApplicationError(e, e.getCode(), e.getMessage()); + } catch (IOException e) { + Log.w(TAG, "An unknown error occurred.", e); + return ServiceResponse.forUnknownError(e); + } } private interface Producer { diff --git a/libsignal/service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java b/libsignal/service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java index dd032ca96..9f7a72a29 100644 --- a/libsignal/service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java +++ b/libsignal/service/src/test/java/org/whispersystems/signalservice/api/services/DonationsServiceTest.java @@ -1,6 +1,5 @@ package org.whispersystems.signalservice.api.services; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -11,26 +10,18 @@ import org.whispersystems.signalservice.api.subscriptions.SubscriberId; import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.push.PushServiceSocket; -import io.reactivex.rxjava3.observers.TestObserver; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.TestScheduler; - +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(JUnit4.class) public class DonationsServiceTest { - private static final TestScheduler TEST_SCHEDULER = new TestScheduler(); - private final PushServiceSocket pushServiceSocket = Mockito.mock(PushServiceSocket.class); private final DonationsService testSubject = new DonationsService(pushServiceSocket); - @BeforeClass - public static void setUpClass() { - RxJavaPlugins.setIoSchedulerHandler(scheduler -> TEST_SCHEDULER); - } - @Test public void givenASubscriberId_whenIGetASuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndNonEmptyObject() throws Exception { // GIVEN @@ -39,12 +30,12 @@ public class DonationsServiceTest { .thenReturn(getActiveSubscription()); // WHEN - TestObserver> testObserver = testSubject.getSubscription(subscriberId).test(); + ServiceResponse response = testSubject.getSubscription(subscriberId); // THEN - TEST_SCHEDULER.triggerActions(); verify(pushServiceSocket).getSubscription(subscriberId.serialize()); - testObserver.assertComplete().assertValue(value -> value.getStatus() == 200 && value.getResult().isPresent()); + assertEquals(200, response.getStatus()); + assertTrue(response.getResult().isPresent()); } @Test @@ -55,12 +46,12 @@ public class DonationsServiceTest { .thenThrow(new NonSuccessfulResponseCodeException(403)); // WHEN - TestObserver> testObserver = testSubject.getSubscription(subscriberId).test(); + ServiceResponse response = testSubject.getSubscription(subscriberId); // THEN - TEST_SCHEDULER.triggerActions(); verify(pushServiceSocket).getSubscription(subscriberId.serialize()); - testObserver.assertComplete().assertValue(value -> value.getStatus() == 403 && !value.getResult().isPresent()); + assertEquals(403, response.getStatus()); + assertFalse(response.getResult().isPresent()); } private ActiveSubscription getActiveSubscription() {