Allow remote config to send non-boolean values
This version of remote config allows non-boolean values to be returned to clients but unfortunately limits the configuration to only one value or another. There is no way to configure more than two values for the same key with this setup.
This commit is contained in:
parent
f39a5f6e68
commit
50ccfee201
|
@ -2,6 +2,7 @@ package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
|
||||||
import com.codahale.metrics.annotation.Timed;
|
import com.codahale.metrics.annotation.Timed;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import io.dropwizard.auth.Auth;
|
||||||
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
|
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
|
||||||
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
|
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
|
@ -29,8 +30,6 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import io.dropwizard.auth.Auth;
|
|
||||||
|
|
||||||
@Path("/v1/config")
|
@Path("/v1/config")
|
||||||
public class RemoteConfigController {
|
public class RemoteConfigController {
|
||||||
|
|
||||||
|
@ -50,12 +49,10 @@ public class RemoteConfigController {
|
||||||
try {
|
try {
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||||
|
|
||||||
return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> new UserRemoteConfig(config.getName(),
|
return new UserRemoteConfigList(remoteConfigsManager.getAll().stream().map(config -> {
|
||||||
isInBucket(digest, account.getUuid(),
|
boolean inBucket = isInBucket(digest, account.getUuid(), config.getName().getBytes(), config.getPercentage(), config.getUuids());
|
||||||
config.getName().getBytes(),
|
return new UserRemoteConfig(config.getName(), inBucket, inBucket ? config.getValue() : config.getDefaultValue());
|
||||||
config.getPercentage(),
|
}).collect(Collectors.toList()));
|
||||||
config.getUuids())))
|
|
||||||
.collect(Collectors.toList()));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
@ -104,5 +101,4 @@ public class RemoteConfigController {
|
||||||
private boolean isAuthorized(String configToken) {
|
private boolean isAuthorized(String configToken) {
|
||||||
return configToken != null && configAuthTokens.stream().anyMatch(authorized -> MessageDigest.isEqual(authorized.getBytes(), configToken.getBytes()));
|
return configToken != null && configAuthTokens.stream().anyMatch(authorized -> MessageDigest.isEqual(authorized.getBytes(), configToken.getBytes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,20 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
public class UserRemoteConfig {
|
public class UserRemoteConfig {
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String value;
|
||||||
|
|
||||||
public UserRemoteConfig() {}
|
public UserRemoteConfig() {}
|
||||||
|
|
||||||
public UserRemoteConfig(String name, boolean enabled) {
|
public UserRemoteConfig(String name, boolean enabled, String value) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -24,4 +28,8 @@ public class UserRemoteConfig {
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@ import javax.validation.constraints.Min;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -28,12 +26,20 @@ public class RemoteConfig {
|
||||||
@NotNull
|
@NotNull
|
||||||
private Set<UUID> uuids = new HashSet<>();
|
private Set<UUID> uuids = new HashSet<>();
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String defaultValue;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String value;
|
||||||
|
|
||||||
public RemoteConfig() {}
|
public RemoteConfig() {}
|
||||||
|
|
||||||
public RemoteConfig(String name, int percentage, Set<UUID> uuids) {
|
public RemoteConfig(String name, int percentage, Set<UUID> uuids, String defaultValue, String value) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.percentage = percentage;
|
this.percentage = percentage;
|
||||||
this.uuids = uuids;
|
this.uuids = uuids;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPercentage() {
|
public int getPercentage() {
|
||||||
|
@ -47,4 +53,12 @@ public class RemoteConfig {
|
||||||
public Set<UUID> getUuids() {
|
public Set<UUID> getUuids() {
|
||||||
return uuids;
|
return uuids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDefaultValue() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,27 +3,22 @@ package org.whispersystems.textsecuregcm.storage;
|
||||||
import com.codahale.metrics.MetricRegistry;
|
import com.codahale.metrics.MetricRegistry;
|
||||||
import com.codahale.metrics.SharedMetricRegistries;
|
import com.codahale.metrics.SharedMetricRegistries;
|
||||||
import com.codahale.metrics.Timer;
|
import com.codahale.metrics.Timer;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import org.jdbi.v3.core.transaction.TransactionIsolationLevel;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.mappers.AccountRowMapper;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.mappers.RemoteConfigRowMapper;
|
import org.whispersystems.textsecuregcm.storage.mappers.RemoteConfigRowMapper;
|
||||||
import org.whispersystems.textsecuregcm.util.Constants;
|
import org.whispersystems.textsecuregcm.util.Constants;
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
|
|
||||||
public class RemoteConfigs {
|
public class RemoteConfigs {
|
||||||
|
|
||||||
public static final String ID = "id";
|
public static final String ID = "id";
|
||||||
public static final String NAME = "name";
|
public static final String NAME = "name";
|
||||||
public static final String PERCENTAGE = "percentage";
|
public static final String PERCENTAGE = "percentage";
|
||||||
public static final String UUIDS = "uuids";
|
public static final String UUIDS = "uuids";
|
||||||
|
public static final String DEFAULT_VALUE = "default_value";
|
||||||
|
public static final String VALUE = "value";
|
||||||
|
|
||||||
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
|
||||||
private final Timer setTimer = metricRegistry.timer(name(Accounts.class, "set" ));
|
private final Timer setTimer = metricRegistry.timer(name(Accounts.class, "set" ));
|
||||||
|
@ -41,11 +36,13 @@ public class RemoteConfigs {
|
||||||
public void set(RemoteConfig remoteConfig) {
|
public void set(RemoteConfig remoteConfig) {
|
||||||
database.use(jdbi -> jdbi.useHandle(handle -> {
|
database.use(jdbi -> jdbi.useHandle(handle -> {
|
||||||
try (Timer.Context ignored = setTimer.time()) {
|
try (Timer.Context ignored = setTimer.time()) {
|
||||||
handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ") VALUES (:name, :percentage, :uuids) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS)
|
handle.createUpdate("INSERT INTO remote_config (" + NAME + ", " + PERCENTAGE + ", " + UUIDS + ", " + DEFAULT_VALUE + ", " + VALUE + ") VALUES (:name, :percentage, :uuids, :default_value, :value) ON CONFLICT(" + NAME + ") DO UPDATE SET " + PERCENTAGE + " = EXCLUDED." + PERCENTAGE + ", " + UUIDS + " = EXCLUDED." + UUIDS + ", " + DEFAULT_VALUE + " = EXCLUDED." + DEFAULT_VALUE + ", " + VALUE + " = EXCLUDED." + VALUE)
|
||||||
.bind("name", remoteConfig.getName())
|
.bind("name", remoteConfig.getName())
|
||||||
.bind("percentage", remoteConfig.getPercentage())
|
.bind("percentage", remoteConfig.getPercentage())
|
||||||
.bind("uuids", remoteConfig.getUuids().toArray(new UUID[0]))
|
.bind("uuids", remoteConfig.getUuids().toArray(new UUID[0]))
|
||||||
.execute();
|
.bind("default_value", remoteConfig.getDefaultValue())
|
||||||
|
.bind("value", remoteConfig.getValue())
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.whispersystems.textsecuregcm.storage;
|
package org.whispersystems.textsecuregcm.storage;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import io.dropwizard.lifecycle.Managed;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
@ -10,8 +11,6 @@ import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import io.dropwizard.lifecycle.Managed;
|
|
||||||
|
|
||||||
public class RemoteConfigsManager implements Managed {
|
public class RemoteConfigsManager implements Managed {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(RemoteConfigsManager.class);
|
private final Logger logger = LoggerFactory.getLogger(RemoteConfigsManager.class);
|
||||||
|
@ -19,7 +18,7 @@ public class RemoteConfigsManager implements Managed {
|
||||||
private final RemoteConfigs remoteConfigs;
|
private final RemoteConfigs remoteConfigs;
|
||||||
private final long sleepInterval;
|
private final long sleepInterval;
|
||||||
|
|
||||||
private AtomicReference<List<RemoteConfig>> cachedConfigs = new AtomicReference<>(new LinkedList<>());
|
private final AtomicReference<List<RemoteConfig>> cachedConfigs = new AtomicReference<>(new LinkedList<>());
|
||||||
|
|
||||||
public RemoteConfigsManager(RemoteConfigs remoteConfigs) {
|
public RemoteConfigsManager(RemoteConfigs remoteConfigs) {
|
||||||
this(remoteConfigs, TimeUnit.SECONDS.toMillis(10));
|
this(remoteConfigs, TimeUnit.SECONDS.toMillis(10));
|
||||||
|
|
|
@ -16,6 +16,10 @@ public class RemoteConfigRowMapper implements RowMapper<RemoteConfig> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RemoteConfig map(ResultSet rs, StatementContext ctx) throws SQLException {
|
public RemoteConfig map(ResultSet rs, StatementContext ctx) throws SQLException {
|
||||||
return new RemoteConfig(rs.getString(RemoteConfigs.NAME), rs.getInt(RemoteConfigs.PERCENTAGE), new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray())));
|
return new RemoteConfig(rs.getString(RemoteConfigs.NAME),
|
||||||
|
rs.getInt(RemoteConfigs.PERCENTAGE),
|
||||||
|
new HashSet<>(Arrays.asList((UUID[])rs.getArray(RemoteConfigs.UUIDS).getArray())),
|
||||||
|
rs.getString(RemoteConfigs.DEFAULT_VALUE),
|
||||||
|
rs.getString(RemoteConfigs.VALUE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,5 +310,12 @@
|
||||||
<changeSet id="16" author="moxie">
|
<changeSet id="16" author="moxie">
|
||||||
<dropColumn tableName="keys" columnName="deleted"/>
|
<dropColumn tableName="keys" columnName="deleted"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet id="17" author="ehren">
|
||||||
|
<addColumn tableName="remote_config">
|
||||||
|
<column name="default_value" type="text"/>
|
||||||
|
<column name="value" type="text"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.whispersystems.textsecuregcm.tests.controllers;
|
package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||||
|
import io.dropwizard.testing.junit.ResourceTestRule;
|
||||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -20,26 +22,25 @@ import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import io.dropwizard.testing.junit.ResourceTestRule;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class RemoteConfigControllerTest {
|
public class RemoteConfigControllerTest {
|
||||||
|
|
||||||
private RemoteConfigsManager remoteConfigsManager = mock(RemoteConfigsManager.class);
|
private final RemoteConfigsManager remoteConfigsManager = mock(RemoteConfigsManager.class);
|
||||||
private List<String> remoteConfigsAuth = new LinkedList<>() {{
|
private final List<String> remoteConfigsAuth = List.of("foo", "bar");
|
||||||
add("foo");
|
|
||||||
add("bar");
|
|
||||||
}};
|
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final ResourceTestRule resources = ResourceTestRule.builder()
|
public final ResourceTestRule resources = ResourceTestRule.builder()
|
||||||
|
@ -52,24 +53,15 @@ public class RemoteConfigControllerTest {
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() {
|
||||||
when(remoteConfigsManager.getAll()).thenReturn(new LinkedList<>() {{
|
when(remoteConfigsManager.getAll()).thenReturn(new LinkedList<>() {{
|
||||||
add(new RemoteConfig("android.stickers", 25, new HashSet<>() {{
|
add(new RemoteConfig("android.stickers", 25, Set.of(AuthHelper.DISABLED_UUID, AuthHelper.INVALID_UUID), null, null));
|
||||||
add(AuthHelper.DISABLED_UUID);
|
add(new RemoteConfig("ios.stickers", 50, Set.of(), null, null));
|
||||||
add(AuthHelper.INVALID_UUID);
|
add(new RemoteConfig("always.true", 100, Set.of(), null, null));
|
||||||
}}));
|
add(new RemoteConfig("only.special", 0, Set.of(AuthHelper.VALID_UUID), null, null));
|
||||||
|
add(new RemoteConfig("value.always.true", 100, Set.of(), "foo", "bar"));
|
||||||
add(new RemoteConfig("ios.stickers", 50, new HashSet<>() {{
|
add(new RemoteConfig("value.only.special", 0, Set.of(AuthHelper.VALID_UUID), "abc", "xyz"));
|
||||||
|
add(new RemoteConfig("value.always.false", 0, Set.of(), "red", "green"));
|
||||||
}}));
|
|
||||||
|
|
||||||
add(new RemoteConfig("always.true", 100, new HashSet<>() {{
|
|
||||||
|
|
||||||
}}));
|
|
||||||
|
|
||||||
add(new RemoteConfig("only.special", 0, new HashSet<>() {{
|
|
||||||
add(AuthHelper.VALID_UUID);
|
|
||||||
}}));
|
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,12 +75,24 @@ public class RemoteConfigControllerTest {
|
||||||
|
|
||||||
verify(remoteConfigsManager, times(1)).getAll();
|
verify(remoteConfigsManager, times(1)).getAll();
|
||||||
|
|
||||||
assertThat(configuration.getConfig().size()).isEqualTo(4);
|
assertThat(configuration.getConfig().size()).isEqualTo(7);
|
||||||
assertThat(configuration.getConfig().get(0).getName()).isEqualTo("android.stickers");
|
assertThat(configuration.getConfig().get(0).getName()).isEqualTo("android.stickers");
|
||||||
assertThat(configuration.getConfig().get(1).getName()).isEqualTo("ios.stickers");
|
assertThat(configuration.getConfig().get(1).getName()).isEqualTo("ios.stickers");
|
||||||
assertThat(configuration.getConfig().get(2).getName()).isEqualTo("always.true");
|
assertThat(configuration.getConfig().get(2).getName()).isEqualTo("always.true");
|
||||||
assertThat(configuration.getConfig().get(2).isEnabled()).isEqualTo(true);
|
assertThat(configuration.getConfig().get(2).isEnabled()).isEqualTo(true);
|
||||||
|
assertThat(configuration.getConfig().get(2).getValue()).isNull();
|
||||||
|
assertThat(configuration.getConfig().get(3).getName()).isEqualTo("only.special");
|
||||||
assertThat(configuration.getConfig().get(3).isEnabled()).isEqualTo(true);
|
assertThat(configuration.getConfig().get(3).isEnabled()).isEqualTo(true);
|
||||||
|
assertThat(configuration.getConfig().get(2).getValue()).isNull();
|
||||||
|
assertThat(configuration.getConfig().get(4).getName()).isEqualTo("value.always.true");
|
||||||
|
assertThat(configuration.getConfig().get(4).isEnabled()).isEqualTo(true);
|
||||||
|
assertThat(configuration.getConfig().get(4).getValue()).isEqualTo("bar");
|
||||||
|
assertThat(configuration.getConfig().get(5).getName()).isEqualTo("value.only.special");
|
||||||
|
assertThat(configuration.getConfig().get(5).isEnabled()).isEqualTo(true);
|
||||||
|
assertThat(configuration.getConfig().get(5).getValue()).isEqualTo("xyz");
|
||||||
|
assertThat(configuration.getConfig().get(6).getName()).isEqualTo("value.always.false");
|
||||||
|
assertThat(configuration.getConfig().get(6).isEnabled()).isEqualTo(false);
|
||||||
|
assertThat(configuration.getConfig().get(6).getValue()).isEqualTo("red");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -101,12 +105,24 @@ public class RemoteConfigControllerTest {
|
||||||
|
|
||||||
verify(remoteConfigsManager, times(1)).getAll();
|
verify(remoteConfigsManager, times(1)).getAll();
|
||||||
|
|
||||||
assertThat(configuration.getConfig().size()).isEqualTo(4);
|
assertThat(configuration.getConfig().size()).isEqualTo(7);
|
||||||
assertThat(configuration.getConfig().get(0).getName()).isEqualTo("android.stickers");
|
assertThat(configuration.getConfig().get(0).getName()).isEqualTo("android.stickers");
|
||||||
assertThat(configuration.getConfig().get(1).getName()).isEqualTo("ios.stickers");
|
assertThat(configuration.getConfig().get(1).getName()).isEqualTo("ios.stickers");
|
||||||
assertThat(configuration.getConfig().get(2).getName()).isEqualTo("always.true");
|
assertThat(configuration.getConfig().get(2).getName()).isEqualTo("always.true");
|
||||||
assertThat(configuration.getConfig().get(2).isEnabled()).isEqualTo(true);
|
assertThat(configuration.getConfig().get(2).isEnabled()).isEqualTo(true);
|
||||||
|
assertThat(configuration.getConfig().get(2).getValue()).isNull();
|
||||||
|
assertThat(configuration.getConfig().get(3).getName()).isEqualTo("only.special");
|
||||||
assertThat(configuration.getConfig().get(3).isEnabled()).isEqualTo(false);
|
assertThat(configuration.getConfig().get(3).isEnabled()).isEqualTo(false);
|
||||||
|
assertThat(configuration.getConfig().get(2).getValue()).isNull();
|
||||||
|
assertThat(configuration.getConfig().get(4).getName()).isEqualTo("value.always.true");
|
||||||
|
assertThat(configuration.getConfig().get(4).isEnabled()).isEqualTo(true);
|
||||||
|
assertThat(configuration.getConfig().get(4).getValue()).isEqualTo("bar");
|
||||||
|
assertThat(configuration.getConfig().get(5).getName()).isEqualTo("value.only.special");
|
||||||
|
assertThat(configuration.getConfig().get(5).isEnabled()).isEqualTo(false);
|
||||||
|
assertThat(configuration.getConfig().get(5).getValue()).isEqualTo("abc");
|
||||||
|
assertThat(configuration.getConfig().get(6).getName()).isEqualTo("value.always.false");
|
||||||
|
assertThat(configuration.getConfig().get(6).isEnabled()).isEqualTo(false);
|
||||||
|
assertThat(configuration.getConfig().get(6).getValue()).isEqualTo("red");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,7 +146,7 @@ public class RemoteConfigControllerTest {
|
||||||
.target("/v1/config")
|
.target("/v1/config")
|
||||||
.request()
|
.request()
|
||||||
.header("Config-Token", "foo")
|
.header("Config-Token", "foo")
|
||||||
.put(Entity.entity(new RemoteConfig("android.stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE));
|
.put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(204);
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
|
|
||||||
|
@ -143,13 +159,32 @@ public class RemoteConfigControllerTest {
|
||||||
assertThat(captor.getValue().getUuids()).isEmpty();
|
assertThat(captor.getValue().getUuids()).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetConfigValued() {
|
||||||
|
Response response = resources.getJerseyTest()
|
||||||
|
.target("/v1/config")
|
||||||
|
.request()
|
||||||
|
.header("Config-Token", "foo")
|
||||||
|
.put(Entity.entity(new RemoteConfig("value.sometimes", 50, Set.of(), "a", "b"), MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
|
|
||||||
|
ArgumentCaptor<RemoteConfig> captor = ArgumentCaptor.forClass(RemoteConfig.class);
|
||||||
|
|
||||||
|
verify(remoteConfigsManager, times(1)).set(captor.capture());
|
||||||
|
|
||||||
|
assertThat(captor.getValue().getName()).isEqualTo("value.sometimes");
|
||||||
|
assertThat(captor.getValue().getPercentage()).isEqualTo(50);
|
||||||
|
assertThat(captor.getValue().getUuids()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetConfigUnauthorized() {
|
public void testSetConfigUnauthorized() {
|
||||||
Response response = resources.getJerseyTest()
|
Response response = resources.getJerseyTest()
|
||||||
.target("/v1/config")
|
.target("/v1/config")
|
||||||
.request()
|
.request()
|
||||||
.header("Config-Token", "baz")
|
.header("Config-Token", "baz")
|
||||||
.put(Entity.entity(new RemoteConfig("android.stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE));
|
.put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(401);
|
assertThat(response.getStatus()).isEqualTo(401);
|
||||||
|
|
||||||
|
@ -161,7 +196,7 @@ public class RemoteConfigControllerTest {
|
||||||
Response response = resources.getJerseyTest()
|
Response response = resources.getJerseyTest()
|
||||||
.target("/v1/config")
|
.target("/v1/config")
|
||||||
.request()
|
.request()
|
||||||
.put(Entity.entity(new RemoteConfig("android.stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE));
|
.put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(401);
|
assertThat(response.getStatus()).isEqualTo(401);
|
||||||
|
|
||||||
|
@ -174,7 +209,7 @@ public class RemoteConfigControllerTest {
|
||||||
.target("/v1/config")
|
.target("/v1/config")
|
||||||
.request()
|
.request()
|
||||||
.header("Config-Token", "foo")
|
.header("Config-Token", "foo")
|
||||||
.put(Entity.entity(new RemoteConfig("android-stickers", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE));
|
.put(Entity.entity(new RemoteConfig("android-stickers", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(422);
|
assertThat(response.getStatus()).isEqualTo(422);
|
||||||
|
|
||||||
|
@ -187,7 +222,7 @@ public class RemoteConfigControllerTest {
|
||||||
.target("/v1/config")
|
.target("/v1/config")
|
||||||
.request()
|
.request()
|
||||||
.header("Config-Token", "foo")
|
.header("Config-Token", "foo")
|
||||||
.put(Entity.entity(new RemoteConfig("", 88, new HashSet<>()), MediaType.APPLICATION_JSON_TYPE));
|
.put(Entity.entity(new RemoteConfig("", 88, Set.of(), "FALSE", "TRUE"), MediaType.APPLICATION_JSON_TYPE));
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(422);
|
assertThat(response.getStatus()).isEqualTo(422);
|
||||||
|
|
||||||
|
@ -232,7 +267,6 @@ public class RemoteConfigControllerTest {
|
||||||
for (int i=0;i<iterations;i++) {
|
for (int i=0;i<iterations;i++) {
|
||||||
for (RemoteConfig config : remoteConfigList) {
|
for (RemoteConfig config : remoteConfigList) {
|
||||||
int count = enabledMap.getOrDefault(config.getName(), 0);
|
int count = enabledMap.getOrDefault(config.getName(), 0);
|
||||||
int random = new SecureRandom().nextInt(iterations);
|
|
||||||
|
|
||||||
if (RemoteConfigController.isInBucket(digest, UUID.randomUUID(), config.getName().getBytes(), config.getPercentage(), new HashSet<>())) {
|
if (RemoteConfigController.isInBucket(digest, UUID.randomUUID(), config.getName().getBytes(), config.getPercentage(), new HashSet<>())) {
|
||||||
count++;
|
count++;
|
||||||
|
|
|
@ -14,11 +14,10 @@ import org.whispersystems.textsecuregcm.storage.RemoteConfigs;
|
||||||
import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager;
|
import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import io.dropwizard.auth.Auth;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
|
||||||
|
|
||||||
public class RemoteConfigsManagerTest {
|
public class RemoteConfigsManagerTest {
|
||||||
|
|
||||||
|
@ -36,17 +35,18 @@ public class RemoteConfigsManagerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdate() throws InterruptedException {
|
public void testUpdate() throws InterruptedException {
|
||||||
remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>() {{
|
remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID), "FALSE", "TRUE"));
|
||||||
add(AuthHelper.VALID_UUID);
|
remoteConfigs.set(new RemoteConfig("value.sometimes", 50, Set.of(), "bar", "baz"));
|
||||||
}}));
|
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(), "FALSE", "TRUE"));
|
||||||
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, new HashSet<>()));
|
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE"));
|
||||||
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, new HashSet<>()));
|
remoteConfigs.set(new RemoteConfig("value.sometimes", 25, Set.of(AuthHelper.VALID_UUID), "abc", "def"));
|
||||||
|
|
||||||
Thread.sleep(501);
|
Thread.sleep(501);
|
||||||
|
|
||||||
List<RemoteConfig> results = remoteConfigs.getAll();
|
List<RemoteConfig> results = remoteConfigs.getAll();
|
||||||
|
|
||||||
assertThat(results.size()).isEqualTo(2);
|
assertThat(results.size()).isEqualTo(3);
|
||||||
|
|
||||||
assertThat(results.get(0).getName()).isEqualTo("android.stickers");
|
assertThat(results.get(0).getName()).isEqualTo("android.stickers");
|
||||||
assertThat(results.get(0).getPercentage()).isEqualTo(50);
|
assertThat(results.get(0).getPercentage()).isEqualTo(50);
|
||||||
assertThat(results.get(0).getUuids().size()).isEqualTo(1);
|
assertThat(results.get(0).getUuids().size()).isEqualTo(1);
|
||||||
|
@ -56,6 +56,13 @@ public class RemoteConfigsManagerTest {
|
||||||
assertThat(results.get(1).getPercentage()).isEqualTo(75);
|
assertThat(results.get(1).getPercentage()).isEqualTo(75);
|
||||||
assertThat(results.get(1).getUuids()).isEmpty();
|
assertThat(results.get(1).getUuids()).isEmpty();
|
||||||
|
|
||||||
|
assertThat(results.get(2).getName()).isEqualTo("value.sometimes");
|
||||||
|
assertThat(results.get(2).getUuids()).hasSize(1);
|
||||||
|
assertThat(results.get(2).getUuids()).contains(AuthHelper.VALID_UUID);
|
||||||
|
assertThat(results.get(2).getPercentage()).isEqualTo(25);
|
||||||
|
assertThat(results.get(2).getDefaultValue()).isEqualTo("abc");
|
||||||
|
assertThat(results.get(2).getValue()).isEqualTo("def");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,10 @@ import org.whispersystems.textsecuregcm.storage.RemoteConfig;
|
||||||
import org.whispersystems.textsecuregcm.storage.RemoteConfigs;
|
import org.whispersystems.textsecuregcm.storage.RemoteConfigs;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import io.dropwizard.auth.Auth;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
|
||||||
|
|
||||||
public class RemoteConfigsTest {
|
public class RemoteConfigsTest {
|
||||||
|
|
||||||
|
@ -33,60 +31,84 @@ public class RemoteConfigsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStore() throws SQLException {
|
public void testStore() {
|
||||||
remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>() {{
|
remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID, AuthHelper.VALID_UUID_TWO), "FALSE", "TRUE"));
|
||||||
add(AuthHelper.VALID_UUID);
|
remoteConfigs.set(new RemoteConfig("value.sometimes", 25, Set.of(AuthHelper.VALID_UUID_TWO), "default", "custom"));
|
||||||
add(AuthHelper.VALID_UUID_TWO);
|
|
||||||
}}));
|
|
||||||
|
|
||||||
List<RemoteConfig> configs = remoteConfigs.getAll();
|
List<RemoteConfig> configs = remoteConfigs.getAll();
|
||||||
|
|
||||||
assertThat(configs.size()).isEqualTo(1);
|
assertThat(configs).hasSize(2);
|
||||||
|
|
||||||
assertThat(configs.get(0).getName()).isEqualTo("android.stickers");
|
assertThat(configs.get(0).getName()).isEqualTo("android.stickers");
|
||||||
|
assertThat(configs.get(0).getValue()).isEqualTo("TRUE");
|
||||||
|
assertThat(configs.get(0).getDefaultValue()).isEqualTo("FALSE");
|
||||||
assertThat(configs.get(0).getPercentage()).isEqualTo(50);
|
assertThat(configs.get(0).getPercentage()).isEqualTo(50);
|
||||||
assertThat(configs.get(0).getUuids().size()).isEqualTo(2);
|
assertThat(configs.get(0).getUuids()).hasSize(2);
|
||||||
assertThat(configs.get(0).getUuids().contains(AuthHelper.VALID_UUID)).isTrue();
|
assertThat(configs.get(0).getUuids()).contains(AuthHelper.VALID_UUID);
|
||||||
assertThat(configs.get(0).getUuids().contains(AuthHelper.VALID_UUID_TWO)).isTrue();
|
assertThat(configs.get(0).getUuids()).contains(AuthHelper.VALID_UUID_TWO);
|
||||||
assertThat(configs.get(0).getUuids().contains(AuthHelper.INVALID_UUID)).isFalse();
|
assertThat(configs.get(0).getUuids()).doesNotContain(AuthHelper.INVALID_UUID);
|
||||||
|
|
||||||
|
assertThat(configs.get(1).getName()).isEqualTo("value.sometimes");
|
||||||
|
assertThat(configs.get(1).getValue()).isEqualTo("custom");
|
||||||
|
assertThat(configs.get(1).getDefaultValue()).isEqualTo("default");
|
||||||
|
assertThat(configs.get(1).getPercentage()).isEqualTo(25);
|
||||||
|
assertThat(configs.get(1).getUuids()).hasSize(1);
|
||||||
|
assertThat(configs.get(1).getUuids()).contains(AuthHelper.VALID_UUID_TWO);
|
||||||
|
assertThat(configs.get(1).getUuids()).doesNotContain(AuthHelper.VALID_UUID);
|
||||||
|
assertThat(configs.get(1).getUuids()).doesNotContain(AuthHelper.INVALID_UUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdate() throws SQLException {
|
public void testUpdate() {
|
||||||
remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>()));
|
remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(), "FALSE", "TRUE"));
|
||||||
|
remoteConfigs.set(new RemoteConfig("value.sometimes", 22, Set.of(), "def", "!"));
|
||||||
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, new HashSet<>() {{
|
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(AuthHelper.DISABLED_UUID), "FALSE", "TRUE"));
|
||||||
add(AuthHelper.DISABLED_UUID);
|
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE"));
|
||||||
}}));
|
remoteConfigs.set(new RemoteConfig("value.sometimes", 77, Set.of(), "hey", "wut"));
|
||||||
|
|
||||||
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, new HashSet<>()));
|
|
||||||
|
|
||||||
List<RemoteConfig> configs = remoteConfigs.getAll();
|
List<RemoteConfig> configs = remoteConfigs.getAll();
|
||||||
|
|
||||||
assertThat(configs.size()).isEqualTo(2);
|
assertThat(configs).hasSize(3);
|
||||||
|
|
||||||
assertThat(configs.get(0).getName()).isEqualTo("android.stickers");
|
assertThat(configs.get(0).getName()).isEqualTo("android.stickers");
|
||||||
assertThat(configs.get(0).getPercentage()).isEqualTo(50);
|
assertThat(configs.get(0).getPercentage()).isEqualTo(50);
|
||||||
assertThat(configs.get(0).getUuids().size()).isEqualTo(0);
|
assertThat(configs.get(0).getUuids()).isEmpty();
|
||||||
|
assertThat(configs.get(0).getDefaultValue()).isEqualTo("FALSE");
|
||||||
|
assertThat(configs.get(0).getValue()).isEqualTo("TRUE");
|
||||||
|
|
||||||
assertThat(configs.get(1).getName()).isEqualTo("ios.stickers");
|
assertThat(configs.get(1).getName()).isEqualTo("ios.stickers");
|
||||||
assertThat(configs.get(1).getPercentage()).isEqualTo(75);
|
assertThat(configs.get(1).getPercentage()).isEqualTo(75);
|
||||||
assertThat(configs.get(1).getUuids().size()).isEqualTo(0);
|
assertThat(configs.get(1).getUuids()).isEmpty();
|
||||||
|
assertThat(configs.get(1).getDefaultValue()).isEqualTo("FALSE");
|
||||||
|
assertThat(configs.get(1).getValue()).isEqualTo("TRUE");
|
||||||
|
|
||||||
|
assertThat(configs.get(2).getName()).isEqualTo("value.sometimes");
|
||||||
|
assertThat(configs.get(2).getPercentage()).isEqualTo(77);
|
||||||
|
assertThat(configs.get(2).getUuids()).isEmpty();
|
||||||
|
assertThat(configs.get(2).getDefaultValue()).isEqualTo("hey");
|
||||||
|
assertThat(configs.get(2).getValue()).isEqualTo("wut");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDelete() {
|
public void testDelete() {
|
||||||
remoteConfigs.set(new RemoteConfig("android.stickers", 50, new HashSet<>() {{
|
remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(AuthHelper.VALID_UUID), "FALSE", "TRUE"));
|
||||||
add(AuthHelper.VALID_UUID);
|
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(), "FALSE", "TRUE"));
|
||||||
}}));
|
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE"));
|
||||||
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, new HashSet<>()));
|
remoteConfigs.set(new RemoteConfig("value.always", 100, Set.of(), "never", "always"));
|
||||||
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, new HashSet<>()));
|
|
||||||
remoteConfigs.delete("android.stickers");
|
remoteConfigs.delete("android.stickers");
|
||||||
|
|
||||||
List<RemoteConfig> configs = remoteConfigs.getAll();
|
List<RemoteConfig> configs = remoteConfigs.getAll();
|
||||||
|
|
||||||
assertThat(configs.size()).isEqualTo(1);
|
assertThat(configs).hasSize(2);
|
||||||
|
|
||||||
assertThat(configs.get(0).getName()).isEqualTo("ios.stickers");
|
assertThat(configs.get(0).getName()).isEqualTo("ios.stickers");
|
||||||
assertThat(configs.get(0).getPercentage()).isEqualTo(75);
|
assertThat(configs.get(0).getPercentage()).isEqualTo(75);
|
||||||
|
assertThat(configs.get(0).getDefaultValue()).isEqualTo("FALSE");
|
||||||
|
assertThat(configs.get(0).getValue()).isEqualTo("TRUE");
|
||||||
|
|
||||||
|
assertThat(configs.get(1).getName()).isEqualTo("value.always");
|
||||||
|
assertThat(configs.get(1).getPercentage()).isEqualTo(100);
|
||||||
|
assertThat(configs.get(1).getValue()).isEqualTo("always");
|
||||||
|
assertThat(configs.get(1).getDefaultValue()).isEqualTo("never");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue