set profile: move updated badge calculation into account updater lambda

This commit is contained in:
Chris Eager 2025-04-11 13:33:32 -05:00 committed by Chris Eager
parent eb71e30046
commit b236b53dc3
4 changed files with 37 additions and 23 deletions

View File

@ -97,21 +97,22 @@ public class DonationController {
return redeemedReceiptsManager.put( return redeemedReceiptsManager.put(
receiptSerial, receiptExpiration.getEpochSecond(), receiptLevel, auth.getAccount().getUuid()) receiptSerial, receiptExpiration.getEpochSecond(), receiptLevel, auth.getAccount().getUuid())
.thenCompose(receiptMatched -> { .thenCompose(receiptMatched -> {
if (!receiptMatched) { if (!receiptMatched) {
return CompletableFuture.completedFuture(
Response.status(Status.BAD_REQUEST).entity("receipt serial is already redeemed")
.type(MediaType.TEXT_PLAIN_TYPE).build());
}
return accountsManager.getByAccountIdentifierAsync(auth.getAccount().getUuid()) return CompletableFuture.completedFuture(
.thenCompose(optionalAccount -> Response.status(Status.BAD_REQUEST).entity("receipt serial is already redeemed")
optionalAccount.map(account -> accountsManager.updateAsync(account, a -> { .type(MediaType.TEXT_PLAIN_TYPE).build());
a.addBadge(clock, new AccountBadge(badgeId, receiptExpiration, request.isVisible())); }
if (request.isPrimary()) {
a.makeBadgePrimaryIfExists(clock, badgeId); return accountsManager.getByAccountIdentifierAsync(auth.getAccount().getUuid())
} .thenCompose(optionalAccount ->
})).orElse(CompletableFuture.completedFuture(null))) optionalAccount.map(account -> accountsManager.updateAsync(account, a -> {
.thenApply(ignored -> Response.ok().build()); a.addBadge(clock, new AccountBadge(badgeId, receiptExpiration, request.isVisible()));
if (request.isPrimary()) {
a.makeBadgePrimaryIfExists(clock, badgeId);
}
})).orElse(CompletableFuture.completedFuture(null)))
.thenApply(ignored -> Response.ok().build());
}); });
}).thenCompose(Function.identity()); }).thenCompose(Function.identity());
} }

View File

@ -204,11 +204,12 @@ public class ProfileController {
.build())); .build()));
} }
final List<AccountBadge> updatedBadges = request.badges()
.map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, auth.getAccount().getBadges()))
.orElseGet(() -> auth.getAccount().getBadges());
accountsManager.update(auth.getAccount(), a -> { accountsManager.update(auth.getAccount(), a -> {
final List<AccountBadge> updatedBadges = request.badges()
.map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, a.getBadges()))
.orElseGet(a::getBadges);
a.setBadges(clock, updatedBadges); a.setBadges(clock, updatedBadges);
a.setCurrentProfileVersion(request.version()); a.setCurrentProfileVersion(request.version());
}); });

View File

@ -145,11 +145,13 @@ public class ProfileGrpcService extends ReactorProfileGrpc.ProfileImplBase {
request.getCommitment().toByteArray()))); request.getCommitment().toByteArray())));
final List<Mono<?>> updates = new ArrayList<>(2); final List<Mono<?>> updates = new ArrayList<>(2);
final List<AccountBadge> updatedBadges = Optional.of(request.getBadgeIdsList())
.map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, account.getBadges()))
.orElseGet(account::getBadges);
updates.add(Mono.fromFuture(() -> accountsManager.updateAsync(account, a -> { updates.add(Mono.fromFuture(() -> accountsManager.updateAsync(account, a -> {
final List<AccountBadge> updatedBadges = Optional.of(request.getBadgeIdsList())
.map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, a.getBadges()))
.orElseGet(a::getBadges);
a.setBadges(clock, updatedBadges); a.setBadges(clock, updatedBadges);
a.setCurrentProfileVersion(request.getVersion()); a.setCurrentProfileVersion(request.getVersion());
}))); })));

View File

@ -19,7 +19,6 @@ import static org.whispersystems.textsecuregcm.tests.util.DevicesHelper.createDe
import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonFilter;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -187,7 +186,7 @@ class AccountTest {
@Test @Test
void addAndRemoveBadges() { void addAndRemoveBadges() {
final Account account = AccountsHelper.generateTestAccount("+14151234567", UUID.randomUUID(), UUID.randomUUID(), List.of(createDevice(Device.PRIMARY_ID)), new byte[0]); final Account account = AccountsHelper.generateTestAccount("+14151234567", UUID.randomUUID(), UUID.randomUUID(), List.of(createDevice(Device.PRIMARY_ID)), new byte[0]);
final Clock clock = TestClock.pinned(Instant.ofEpochSecond(40)); final TestClock clock = TestClock.pinned(Instant.ofEpochSecond(40));
account.addBadge(clock, new AccountBadge("foo", Instant.ofEpochSecond(42), false)); account.addBadge(clock, new AccountBadge("foo", Instant.ofEpochSecond(42), false));
account.addBadge(clock, new AccountBadge("bar", Instant.ofEpochSecond(44), true)); account.addBadge(clock, new AccountBadge("bar", Instant.ofEpochSecond(44), true));
@ -214,6 +213,17 @@ class AccountTest {
assertThat(badge.expiration().getEpochSecond()).isEqualTo(51); assertThat(badge.expiration().getEpochSecond()).isEqualTo(51);
assertThat(badge.visible()).isTrue(); assertThat(badge.visible()).isTrue();
}); });
clock.pin(Instant.ofEpochSecond(52));
// for a merged badge, visible = true is preferred
account.addBadge(clock, new AccountBadge("foo", Instant.ofEpochSecond(53), false));
assertThat(account.getBadges()).hasSize(1).element(0).satisfies(badge -> {
assertThat(badge.id()).isEqualTo("foo");
assertThat(badge.expiration().getEpochSecond()).isEqualTo(53);
assertThat(badge.visible()).isTrue();
});
} }
@Test @Test