mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Remove blocking get from donation jobs.
This commit is contained in:
committed by
Greyson Parrelli
parent
8f12b2041a
commit
f50bf3e9c2
@@ -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<ServiceResponse<EmptyResponse>> 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.<EmptyResponse>forUnknownError(e);
|
||||
}
|
||||
}).subscribeOn(Schedulers.io());
|
||||
public ServiceResponse<EmptyResponse> 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.<EmptyResponse>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<ServiceResponse<SubscriptionClientSecret>> createDonationIntentWithAmount(String amount, String currencyCode, long level) {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.createBoostPaymentMethod(currencyCode, Long.parseLong(amount), level), 200));
|
||||
public ServiceResponse<SubscriptionClientSecret> 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<ServiceResponse<ReceiptCredentialResponse>> submitBoostReceiptCredentialRequest(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest) {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.submitBoostReceiptCredentials(paymentIntentId, receiptCredentialRequest), 200));
|
||||
public ServiceResponse<ReceiptCredentialResponse> submitBoostReceiptCredentialRequestSync(String paymentIntentId, ReceiptCredentialRequest receiptCredentialRequest) {
|
||||
return wrapInServiceResponse(() -> new Pair<>(pushServiceSocket.submitBoostReceiptCredentials(paymentIntentId, receiptCredentialRequest), 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The suggested amounts for Signal Boost
|
||||
*/
|
||||
public Single<ServiceResponse<Map<String, List<BigDecimal>>>> getBoostAmounts() {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostAmounts(), 200));
|
||||
public ServiceResponse<Map<String, List<BigDecimal>>> 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<ServiceResponse<SignalServiceProfile.Badge>> getBoostBadge(Locale locale) {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(SubscriptionLevels.BOOST_LEVEL).getBadge(), 200));
|
||||
public ServiceResponse<SignalServiceProfile.Badge> 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<ServiceResponse<SignalServiceProfile.Badge>> getGiftBadge(Locale locale, long level) {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getBoostLevels(locale).getLevels().get(String.valueOf(level)).getBadge(), 200));
|
||||
public ServiceResponse<SignalServiceProfile.Badge> 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<ServiceResponse<Map<Long, SignalServiceProfile.Badge>>> getGiftBadges(Locale locale) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<Map<Long, SignalServiceProfile.Badge>> getGiftBadges(Locale locale) {
|
||||
return wrapInServiceResponse(() -> {
|
||||
Map<String, SubscriptionLevels.Level> levels = pushServiceSocket.getBoostLevels(locale).getLevels();
|
||||
Map<Long, SignalServiceProfile.Badge> badges = new TreeMap<>();
|
||||
|
||||
@@ -141,15 +136,15 @@ public class DonationsService {
|
||||
/**
|
||||
* Returns the amounts for the gift badge.
|
||||
*/
|
||||
public Single<ServiceResponse<Map<String, BigDecimal>>> getGiftAmount() {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getGiftAmount(), 200));
|
||||
public ServiceResponse<Map<String, BigDecimal>> 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<ServiceResponse<SubscriptionLevels>> getSubscriptionLevels(Locale locale) {
|
||||
return createServiceResponse(() -> new Pair<>(pushServiceSocket.getSubscriptionLevels(locale), 200));
|
||||
public ServiceResponse<SubscriptionLevels> 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<ServiceResponse<EmptyResponse>> updateSubscriptionLevel(SubscriberId subscriberId,
|
||||
String level,
|
||||
String currencyCode,
|
||||
String idempotencyKey,
|
||||
Object mutex
|
||||
public ServiceResponse<EmptyResponse> 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<ServiceResponse<ActiveSubscription>> getSubscription(SubscriberId subscriberId) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<ActiveSubscription> 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<ServiceResponse<EmptyResponse>> putSubscription(SubscriberId subscriberId) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<EmptyResponse> 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<ServiceResponse<EmptyResponse>> cancelSubscription(SubscriberId subscriberId) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<EmptyResponse> cancelSubscription(SubscriberId subscriberId) {
|
||||
return wrapInServiceResponse(() -> {
|
||||
pushServiceSocket.deleteSubscription(subscriberId.serialize());
|
||||
return new Pair<>(EmptyResponse.INSTANCE, 200);
|
||||
});
|
||||
}
|
||||
|
||||
public Single<ServiceResponse<EmptyResponse>> setDefaultPaymentMethodId(SubscriberId subscriberId, String paymentMethodId) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<EmptyResponse> 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<ServiceResponse<SubscriptionClientSecret>> createSubscriptionPaymentMethod(SubscriberId subscriberId) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<SubscriptionClientSecret> createSubscriptionPaymentMethod(SubscriberId subscriberId) {
|
||||
return wrapInServiceResponse(() -> {
|
||||
SubscriptionClientSecret clientSecret = pushServiceSocket.createSubscriptionPaymentMethod(subscriberId.serialize());
|
||||
return new Pair<>(clientSecret, 200);
|
||||
});
|
||||
}
|
||||
|
||||
public Single<ServiceResponse<ReceiptCredentialResponse>> submitReceiptCredentialRequest(SubscriberId subscriberId, ReceiptCredentialRequest receiptCredentialRequest) {
|
||||
return createServiceResponse(() -> {
|
||||
public ServiceResponse<ReceiptCredentialResponse> submitReceiptCredentialRequestSync(SubscriberId subscriberId, ReceiptCredentialRequest receiptCredentialRequest) {
|
||||
return wrapInServiceResponse(() -> {
|
||||
ReceiptCredentialResponse response = pushServiceSocket.submitReceiptCredentials(subscriberId.serialize(), receiptCredentialRequest);
|
||||
return new Pair<>(response, 200);
|
||||
});
|
||||
}
|
||||
|
||||
private <T> Single<ServiceResponse<T>> createServiceResponse(Producer<T> producer) {
|
||||
return Single.fromCallable(() -> {
|
||||
try {
|
||||
Pair<T, Integer> 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 <T> ServiceResponse<T> wrapInServiceResponse(Producer<T> producer) {
|
||||
try {
|
||||
Pair<T, Integer> 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<T> {
|
||||
|
||||
@@ -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<ServiceResponse<ActiveSubscription>> testObserver = testSubject.getSubscription(subscriberId).test();
|
||||
ServiceResponse<ActiveSubscription> 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<ServiceResponse<ActiveSubscription>> testObserver = testSubject.getSubscription(subscriberId).test();
|
||||
ServiceResponse<ActiveSubscription> 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() {
|
||||
|
||||
Reference in New Issue
Block a user