Skip to content

Commit

Permalink
Do not resume remote configuration from cache in case it has differen…
Browse files Browse the repository at this point in the history
…t endpoint (close #533)

PR #534
  • Loading branch information
matus-tomlein committed Aug 15, 2022
1 parent 3dcb2e8 commit b358909
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 30 deletions.
Expand Up @@ -117,11 +117,12 @@ public void testConfigurationCache() {
expected.configurationVersion = 12;
expected.configurationBundle = Lists.newArrayList(bundle);

ConfigurationCache cache = new ConfigurationCache();
RemoteConfiguration remoteConfiguration = new RemoteConfiguration("http://example.com", HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfiguration);
cache.clearCache(context);
cache.writeCache(context, expected);

cache = new ConfigurationCache();
cache = new ConfigurationCache(remoteConfiguration);
FetchedConfigurationBundle config = cache.readCache(context);

assertEquals(expected.configurationVersion, config.configurationVersion);
Expand Down Expand Up @@ -161,14 +162,14 @@ public void testConfigurationFetcher_downloads() throws IOException, Interrupted
public void testConfigurationProvider_notDownloading_fails() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
ConfigurationCache cache = new ConfigurationCache();
cache.clearCache(context);
MockWebServer mockWebServer = getMockServer(500, "{}");
String endpoint = getMockServerURI(mockWebServer);
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfig);
cache.clearCache(context);

// test
final Object expectation = new Object();
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
provider.retrieveConfiguration(context, false, pair -> fail());
synchronized (expectation) {
Expand All @@ -181,14 +182,14 @@ public void testConfigurationProvider_notDownloading_fails() throws IOException,
public void testConfigurationProvider_downloadOfWrongSchema_fails() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
ConfigurationCache cache = new ConfigurationCache();
cache.clearCache(context);
MockWebServer mockWebServer = getMockServer(200, "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0\",\"configurationVersion\":12,\"configurationBundle\":[]}");
String endpoint = getMockServerURI(mockWebServer);
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfig);
cache.clearCache(context);

// test
final Object expectation = new Object();
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
provider.retrieveConfiguration(context, false, pair -> fail());
synchronized (expectation) {
Expand All @@ -201,7 +202,10 @@ public void testConfigurationProvider_downloadOfWrongSchema_fails() throws IOExc
public void testConfigurationProvider_downloadSameConfigVersionThanCached_dontUpdate() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
ConfigurationCache cache = new ConfigurationCache();
MockWebServer mockWebServer = getMockServer(200, "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}");
String endpoint = getMockServerURI(mockWebServer);
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfig);
cache.clearCache(context);

ConfigurationBundle bundle = new ConfigurationBundle("namespace");
Expand All @@ -210,12 +214,9 @@ public void testConfigurationProvider_downloadSameConfigVersionThanCached_dontUp
cached.configurationVersion = 1;
cached.configurationBundle = Lists.newArrayList(bundle);
cache.writeCache(context, cached);
MockWebServer mockWebServer = getMockServer(200, "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}");
String endpoint = getMockServerURI(mockWebServer);

// test
final Object expectation = new Object();
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
final int[] i = {0}; // Needed to make it accessible inside the closure.
provider.retrieveConfiguration(context, false, pair -> {
Expand All @@ -239,7 +240,10 @@ public void testConfigurationProvider_downloadSameConfigVersionThanCached_dontUp
public void testConfigurationProvider_downloadHigherConfigVersionThanCached_doUpdate() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
ConfigurationCache cache = new ConfigurationCache();
MockWebServer mockWebServer = getMockServer(200, "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}");
String endpoint = getMockServerURI(mockWebServer);
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfig);
cache.clearCache(context);

ConfigurationBundle bundle = new ConfigurationBundle("namespace");
Expand All @@ -248,12 +252,9 @@ public void testConfigurationProvider_downloadHigherConfigVersionThanCached_doUp
cached.configurationVersion = 1;
cached.configurationBundle = Lists.newArrayList(bundle);
cache.writeCache(context, cached);
MockWebServer mockWebServer = getMockServer(200, "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":2,\"configurationBundle\":[]}");
String endpoint = getMockServerURI(mockWebServer);

// test
final Object expectation = new Object();
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
final int[] i = {0}; // Needed to make it accessible inside the closure.
provider.retrieveConfiguration(context, false, pair -> {
Expand All @@ -280,7 +281,10 @@ public void testConfigurationProvider_downloadHigherConfigVersionThanCached_doUp
public void testConfigurationProvider_justRefresh_downloadSameConfigVersionThanCached_dontUpdate() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
ConfigurationCache cache = new ConfigurationCache();
MockWebServer mockWebServer = getMockServer(404, "{}");
String endpoint = getMockServerURI(mockWebServer);
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfig);
cache.clearCache(context);

ConfigurationBundle bundle = new ConfigurationBundle("namespace");
Expand All @@ -290,11 +294,7 @@ public void testConfigurationProvider_justRefresh_downloadSameConfigVersionThanC
cached.configurationBundle = Lists.newArrayList(bundle);
cache.writeCache(context, cached);

MockWebServer mockWebServer = getMockServer(404, "{}");
String endpoint = getMockServerURI(mockWebServer);

final Object expectation = new Object();
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
final int[] i = {0}; // Needed to make it accessible inside the closure.
provider.retrieveConfiguration(context, false, pair -> {
Expand Down Expand Up @@ -326,7 +326,10 @@ public void testConfigurationProvider_justRefresh_downloadSameConfigVersionThanC
public void testConfigurationProvider_justRefresh_downloadHigherConfigVersionThanCached_doUpdate() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
ConfigurationCache cache = new ConfigurationCache();
MockWebServer mockWebServer = getMockServer(404, "{}");
String endpoint = getMockServerURI(mockWebServer);
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(remoteConfig);
cache.clearCache(context);

ConfigurationBundle bundle = new ConfigurationBundle("namespace");
Expand All @@ -336,11 +339,7 @@ public void testConfigurationProvider_justRefresh_downloadHigherConfigVersionTha
cached.configurationBundle = Lists.newArrayList(bundle);
cache.writeCache(context, cached);

MockWebServer mockWebServer = getMockServer(404, "{}");
String endpoint = getMockServerURI(mockWebServer);

final Object expectation = new Object();
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
final int[] i = {0}; // Needed to make it accessible inside the closure.
provider.retrieveConfiguration(context, false, pair -> {
Expand Down Expand Up @@ -378,6 +377,45 @@ public void testConfigurationProvider_justRefresh_downloadHigherConfigVersionTha
mockWebServer.shutdown();
}

@Test
public void testDoesntUseCachedConfigurationIfDifferentRemoteEndpoint() throws IOException, InterruptedException {
// prepare test
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
RemoteConfiguration cachedRemoteConfig = new RemoteConfiguration("http://cache.example.com", HttpMethod.GET);
ConfigurationCache cache = new ConfigurationCache(cachedRemoteConfig);
cache.clearCache(context);

// write configuration (version 2) to cache
ConfigurationBundle bundle = new ConfigurationBundle("namespace");
bundle.networkConfiguration = new NetworkConfiguration("endpoint");
FetchedConfigurationBundle cached = new FetchedConfigurationBundle("http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-0-0");
cached.configurationVersion = 2;
cached.configurationBundle = Lists.newArrayList(bundle);
cache.writeCache(context, cached);

// stub request for configuration (return version 1)
MockWebServer mockWebServer = getMockServer(200, "{\"$schema\":\"http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0\",\"configurationVersion\":1,\"configurationBundle\":[]}");
String endpoint = getMockServerURI(mockWebServer);

// retrieve remote configuration
RemoteConfiguration remoteConfig = new RemoteConfiguration(endpoint, HttpMethod.GET);
ConfigurationProvider provider = new ConfigurationProvider(remoteConfig);
final int[] numCallbackCalls = {0};
final Object expectation = new Object();
provider.retrieveConfiguration(context, true, pair -> {
FetchedConfigurationBundle fetchedConfigurationBundle = pair.first;
numCallbackCalls[0]++;
// should be the non-cache configuration (version 1)
assertEquals("http://iglucentral.com/schemas/com.snowplowanalytics.mobile/remote_config/jsonschema/1-1-0", fetchedConfigurationBundle.schema);
assertEquals(1, fetchedConfigurationBundle.configurationVersion);
});
synchronized (expectation) {
expectation.wait(5000);
}
assertEquals(1, numCallbackCalls[0]);
mockWebServer.shutdown();
}

// Private methods

public MockWebServer getMockServer(int responseCode, String body) throws IOException {
Expand Down
Expand Up @@ -15,13 +15,13 @@
public class RemoteConfiguration implements Configuration {

/**
* URL (without schema/protocol) used to send events to the collector.
* URL of the remote configuration.
*/
@NonNull
public final String endpoint;

/**
* Method used to send events to the collector.
* The method used to send the requests (GET or POST).
*/
@NonNull
public final HttpMethod method;
Expand Down
Expand Up @@ -5,6 +5,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.snowplowanalytics.snowplow.configuration.RemoteConfiguration;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
Expand All @@ -19,6 +21,12 @@ public class ConfigurationCache {
private String cacheFilePath;
@Nullable
private FetchedConfigurationBundle configuration;
@NonNull
private RemoteConfiguration remoteConfiguration;

public ConfigurationCache(@NonNull RemoteConfiguration remoteConfiguration) {
this.remoteConfiguration = remoteConfiguration;
}

@Nullable
public synchronized FetchedConfigurationBundle readCache(@NonNull Context context) {
Expand Down Expand Up @@ -51,7 +59,8 @@ private String getCachePath(@NonNull Context context) {
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
cacheFilePath = cacheDir.getAbsolutePath() + File.separator + "remoteConfig.data";
String fileName = "remoteConfig-" + remoteConfiguration.endpoint.hashCode() + ".data";
cacheFilePath = cacheDir.getAbsolutePath() + File.separator + fileName;
return cacheFilePath;
}

Expand Down
Expand Up @@ -34,7 +34,7 @@ public ConfigurationProvider(@NonNull RemoteConfiguration remoteConfiguration) {

public ConfigurationProvider(@NonNull RemoteConfiguration remoteConfiguration, @Nullable List<ConfigurationBundle> defaultBundles) {
this.remoteConfiguration = remoteConfiguration;
this.cache = new ConfigurationCache();
this.cache = new ConfigurationCache(remoteConfiguration);
if (defaultBundles != null) {
FetchedConfigurationBundle bundle = new FetchedConfigurationBundle("1.0");
bundle.configurationVersion = Integer.MIN_VALUE;
Expand Down

0 comments on commit b358909

Please sign in to comment.