Implement the ProfileBadgeConverter interface

This commit is contained in:
Ehren Kret
2021-09-15 10:32:20 -05:00
committed by GitHub
parent 5f8accb492
commit 79ad09524e
4 changed files with 331 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import com.google.common.annotations.VisibleForTesting;
import java.time.Clock;
import java.time.Instant;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.entities.Badge;
import org.whispersystems.textsecuregcm.storage.AccountBadge;
public class ConfiguredProfileBadgeConverter implements ProfileBadgeConverter {
private static final int MAX_LOCALES = 15;
@VisibleForTesting
static final String BASE_NAME = "org.signal.badges.Badges";
private final Clock clock;
private final Map<String, BadgeConfiguration> knownBadges;
private final ResourceBundleFactory resourceBundleFactory;
public ConfiguredProfileBadgeConverter(
final Clock clock,
final BadgesConfiguration badgesConfiguration) {
this(clock, badgesConfiguration, ResourceBundle::getBundle);
}
@VisibleForTesting
public ConfiguredProfileBadgeConverter(
final Clock clock,
final BadgesConfiguration badgesConfiguration,
final ResourceBundleFactory resourceBundleFactory) {
this.clock = clock;
this.knownBadges = badgesConfiguration.getBadges().stream()
.collect(Collectors.toMap(BadgeConfiguration::getName, Function.identity()));
this.resourceBundleFactory = resourceBundleFactory;
}
@Override
public Set<Badge> convert(
final List<Locale> acceptableLanguages,
final Set<AccountBadge> accountBadges) {
if (accountBadges.isEmpty()) {
return Set.of();
}
final Instant now = clock.instant();
final List<Locale> acceptableLocales = acceptableLanguages.stream().limit(MAX_LOCALES).distinct()
.collect(Collectors.toList());
final Locale desiredLocale = acceptableLocales.isEmpty() ? Locale.getDefault() : acceptableLocales.get(0);
// define a control with a fallback order as specified in the header
ResourceBundle.Control control = new Control() {
@Override
public List<String> getFormats(final String baseName) {
Objects.requireNonNull(baseName);
return Control.FORMAT_PROPERTIES;
}
@Override
public Locale getFallbackLocale(final String baseName, final Locale locale) {
Objects.requireNonNull(baseName);
if (locale.equals(Locale.getDefault())) {
return null;
}
final int localeIndex = acceptableLocales.indexOf(locale);
if (localeIndex < 0 || localeIndex >= acceptableLocales.size() - 1) {
return Locale.getDefault();
}
// [0, acceptableLocales.size() - 2] is now the possible range for localeIndex
return acceptableLocales.get(localeIndex + 1);
}
};
final ResourceBundle resourceBundle = resourceBundleFactory.createBundle(BASE_NAME, desiredLocale, control);
return accountBadges.stream()
.filter(accountBadge -> accountBadge.isVisible()
&& now.isBefore(accountBadge.getExpiration())
&& knownBadges.containsKey(accountBadge.getName()))
.map(accountBadge -> new Badge(knownBadges.get(accountBadge.getName()).getImageUrl(),
resourceBundle.getString(accountBadge.getName() + "_name"),
resourceBundle.getString(accountBadge.getName() + "_description")))
.collect(Collectors.toSet());
}
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import java.util.Locale;
import java.util.ResourceBundle;
public interface ResourceBundleFactory {
ResourceBundle createBundle(String baseName, Locale locale, ResourceBundle.Control control);
}

View File

@@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.net.URL;
import java.util.Objects;
public class Badge {
private final URL imageUrl;
@@ -35,4 +36,22 @@ public class Badge {
public String getDescription() {
return description;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Badge badge = (Badge) o;
return Objects.equals(imageUrl, badge.imageUrl) && Objects.equals(name,
badge.name) && Objects.equals(description, badge.description);
}
@Override
public int hashCode() {
return Objects.hash(imageUrl, name, description);
}
}